Compare commits

...
766 Commits
Author SHA1 Message Date
conradsnicta 799937bda8 rework LLM section as per mlpack
Build and Test / CPU (push) Waiting to run
Build and Test / CUDA (push) Waiting to run
Build and Test / OpenCL (push) Waiting to run
2026-02-02 01:11:15 +01:00
conradsnicta 612fd94c9c add note on code generated via LLMs 2025-12-17 05:39:34 +01:00
Ryan Curtin 30dbb05379 Add new block for next release to HISTORY.md. 2025-12-16 09:48:09 -05:00
Ryan Curtin 356be31685 Update and release version 3.11.0.
Build and Test / CPU (push) Waiting to run
Build and Test / CUDA (push) Waiting to run
Build and Test / OpenCL (push) Waiting to run
2025-12-16 09:48:09 -05:00
Ryan Curtin eba232499f Revert "Release version 3.11.0: "Sunny Day" (#447)" (#448)
This reverts commit db5cef9823.
2025-12-15 08:58:16 -05:00
Ryan Curtin db5cef9823 Release version 3.11.0: "Sunny Day" (#447)
* Update and release version 3.11.0.

* Add new block for next release to HISTORY.md.
2025-12-13 07:46:44 -06:00
Ranjodh SinghandRyan Curtin b5f49dc419 Implement DeltaBarDelta using refactored GradientDescent. (#440)
* Refactor GradientDescent to GradientDescentType<UpdatePolicyType, DecayPolicyType>

* Add DeltaBarDeltaUpdate

* Update Docs

* Add Tests

* Use clamp instead of find and fill

* Try to fix CI Failure

* Update History.md

* Simplify and Correct DeltaBarDelta Description

* Revert inadvertent code change

* Simplify description in optmizer docs too

* initialize isInitialized :)

* Address review suggestions

* Add MomentumDeltaBarDelta

* Change minStepSize to minGain

* Address Review Comments

* Increase test iterations to fix CI failure

* Apply suggestions from code review

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Fix a typo

---------

Co-authored-by: Ryan Curtin <ryan@ratml.org>
2025-12-11 09:26:54 -05:00
arminsch 117f8b8e75 fix missing return in aug_lagrangian (#446) 2025-11-13 09:36:14 -05:00
Ranjodh Singh b4b403e122 Fix an off by one issue affecting number of iterations in multiple optimizers (#443) 2025-11-11 05:33:12 +00:00
Ryan Curtin 9aebc1d2e4 Add new block for next release to HISTORY.md. 2025-09-30 14:02:53 -04:00
Ryan Curtin 136d8136c2 Update and release version 3.10.0.
Build and Test / CPU (push) Waiting to run
Build and Test / CUDA (push) Waiting to run
Build and Test / OpenCL (push) Waiting to run
2025-09-30 14:02:53 -04:00
Xiaochuan YeandRyan Curtin b22e53f3c8 Refactor calculatedObjectives in NSGA2 optimizer (#434)
* Refactor calculatedObjectives in NSGA2 optimizer

Signed-off-by: yexiaochuan <tap91624@gmail.com>

* Use Mat instead of Cube.

---------

Signed-off-by: yexiaochuan <tap91624@gmail.com>
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2025-09-25 08:34:16 -04:00
Ryan Curtin a8f6e7833c Deprecate the Lambda() and Sigma() functions for AugLagrangian (#439)
* Deprecate the Lambda() and Sigma() functions for AugLagrangian.

* Add utility struct to differentiate between matrices and callbacks.

* Update HISTORY.

* Fix overload issues with LRSDP and new AugLagrangian types.

* Fix some documentation to kick off another build.  (I think there was an issue with dealgood.)

* Remove spaces to kick the build off again.
2025-09-25 08:33:51 -04:00
Ryan Curtin 9fa90f1091 Merge Bandicoot tests with other tests (#438)
* Normalize step size to the batch size used.

* Update history.

* Don't use if constexpr.

* Fix all step sizes in tests to account for new behavior.

* Retune DemonSGD tests to reduce failure probability.

* Merge Bandicoot tests into existing TEMPLATE_TEST_CASEs.

* Fix merge artifact.

* Fix compilation errors.

* Fix MOEAD test to be more robust.

* Oops, fix missing colon.

* Fix compilation issues.

* Remove arma:: for randu call.

* Try to fix AppVeyor build.

* Bump version number to fix mlpack integration build.

* Adapt FBS/FISTA/FASTA to bandicoot tests.

* Keep bandicoot implementation separate.

* Print which tests take a long time so I can prune down the runtime.

* Remove some tests that take a long long time.

* Filter out some more long-running GPU tests.
2025-09-22 13:03:14 -04:00
Ryan Curtin 37b057dce0 Add proximal gradient optimizers: FBS, FISTA, and FASTA (#427)
* Implement forward-backward splitting (FBS) and some proximal operators.

* Minor cleanups for FrankWolfe.

* Allow FunctionTest() to take instantiated functions.

* Some minor fixes to some test functions.

* Add tests for FBS and proximal operators.

* Clean up documentation.

* Add documentation for FBS to optimizers.md.

* Add some documentation about proximal operators and functions.

* Fix header guard name.

* Remove unused member.

* Some minor FBS cleanups.

* Add QuadraticFunction for testing.

* Try to clean up L1 constraint application a little bit.

* Add FISTA and tests.

* Document FISTA.

* Remove inaccurate comment.

* Clean up to match Beck and Teboulle paper instead of FASTA paper.

* Some minor efficiency cleanups.

* Add debugged and working FASTA implementation.

* Add documentation for FASTA.

* Make L1Constraint tests robust to the element type.

* Minor fixes for compilation and tests.

* Try to fix AppVeyor build.

* Bump version number to fix mlpack integration build.

* Add changelog entry for FBS/FISTA/FASTA.

* Fixes for FP16 compilation and failing tests.

* Fix compilation issue.

* Add instantiated versions of tolerances for older compilers / C++14.

* Adjust tolerances.

* Try to make MSVC happy with where values are defined vs. declared.

* Make sure that bandicoot is included if needed.
2025-09-18 09:15:57 -04:00
Ryan Curtin 8151354884 Remove deprecated functionality for ensmallen 3.x release. (#435)
* Remove deprecated functionality for the ensmallen 3.x release.

* Bump version so that anyone doing version checks gets correct details.

* Update HISTORY.md.

* Use clearer documentation for how to convert old code.

* Try to fix AppVeyor build.

* Some changes to make the IPOP-CMAES RastriginFunction test faster.
2025-09-16 14:36:08 -04:00
Ryan Curtin 308d1690c7 Fix compilation warnings. (#432) 2025-09-11 14:42:56 -04:00
Ryan Curtin a4ec8b564f Normalize step size to the batch size used (#431)
* Normalize step size to the batch size used.

* Update history.

* Don't use if constexpr.

* Fix all step sizes in tests to account for new behavior.

* Retune DemonSGD tests to reduce failure probability.

* Fix merge artifact.

* Fix MOEAD test to be more robust.
2025-09-08 20:06:05 -04:00
3b4e1e261c Test FP16 support when available with Armadillo (#426)
* Add CMake modules to find Bandicoot libraries and includes.

* Add initial GitHub Actions to build against Bandicoot and run the tests.

* 1. Add type proxies to select the right type based on the used backend and datatype.
2. Update IQN optimizer to build against Armadillo and Bandicoot.
3. Update IQN test cases to test against Armadillo and Bandicoot backend.
4. Update logisitic regression function, to support Armadillo and Bandicoot.

* Refactor CNE optimizer and tests cases to work with Armadillo and Bandicoot datatypes.

* Refactor Katyusha optimizer and tests cases to work with Armadillo and Bandicoot datatypes.

* Refactor optimizers to work with Armadillo and Bandicoot datatypes:

1. AdaBelief
2. AdaBound
3. AMSBound
4. AdaGrad
5. Adam
6. Adamax
7. AMSGrad
8. Nadam
9. Nadamax
10. OptimisticAdam
11. Eve
12. FTML
13. LBFGS
14. Lookahead
15. Padam
16. QHAdam
17. SMORMS3
18. SpaleraSGD
19. SPSA
20. SWATS
21. WNGrad
22. Yogi
23. MomentumSGD
24. NesterovMomentum
25. QuasiHyperbolic
26. SGD
27. SGDR
28. SnapshotSGDR
29. CNE
30. CMAES
31. ActiveCMAES
32. PSO
33. LBestPSO
34. MOEAD
35. AugmentedLagrangian

* Refactor optimizers to work with Armadillo and Bandicoot datatypes:
1. AdaDelta
2. AdaSqrt
3. BigBatchSGD
4. CoordinateDescent (CD)
5. GradientDescent
6. GridSearch
7. NSGA2
8. ParallelSGD

* Refactor optimizers to work with Armadillo and Bandicoot datatypes:
1. DifferentialEvolution (DE)
2. RMSProp
3. SimulatedAnnealing (SA)
4. SARAH
5. SARAH+
6. SVRG
7. BarzilaiBorweinDecay
8. DemonAdam
9. DemonSGD

* Use inverse transform sampling to replicating arma::randg sampling since randg is not supported in Bandicoot.

* Apply suggestions from code review by @rcurtin.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Group test cases and fix indentations.

* Update optimizer reference styling.

* Add option to build against Bandicoot (by default we build against Bandicoot if found).

* Mention AugLagrangianType constructor in the optimizer documentation, that allows to use a different datatype i.e. arma::fvec or coot::fvec.

* Use data type specific element type for the bounds parameter.

* Make sure we follow the max character line limitation from the style guide.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Remove backports for outdates armadillo versions.

* Indentation fixes.

* Indentation fixes.

* Use the correct data type during testing.

* Do not use the namespace twice.

* Test against CPU/OpenCL/CUDA backend.

* Use the correct test case type.

* We don't need Bandicoot when we test against Armadillo (CPU) backend.

* Use the correct minimal Bandicoot version.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Comment on the added proxies.

* Merge arma and coot traits into a single file.

* Fix deprecation warning from newer Armadillo.

* Fix spacing.

* Add logging output for PSO.

* Adapt problems to be templatized.

* Templatize tests to use ENS_TEST_TYPES.

* Fix name of AdaGrad in the documentation.

* Apply suggestions from code review.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Update about half the optimizers to use FP16.

* Refactor ForwardType into two different structures to avoid std::conditional.

* Minor changes to get tests to pass on the Pi.

* Remove unused proxies.

* Update the other half of the optimizers to use FP16.

* Minor fixes to work on non-emulated FP16.

* Update to arma::hmat and enable sparse types.

* Make CMake standard easily configurable.

* Revert CategoryType to arma::Row<size_t> since that should always stay on the CPU.

* Add convenience IsMatrixType<> struct.

* Catch underflow for RMSprop.

* Revert to norm()-only implementation for gradient norm computation.

* Remove proxies, use using instead.

* Older Armadillo versions don't support changing sparse-to-dense and also element types.

* Remove sort_index() proxy and use using instead.

* Oops, fix compatibility conv_to implementation.

* Fix divide by zero condition.

* Individually accessed vector, so keep vector on the CPU, for faster access.

* Apply suggestions from code review

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Kepp the indicies on the CPU.

* Apply suggestions from code review.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Remove outdated test case.

* Use initialization list for the velocity vector.

* Apply suggestions from code review

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Readd removed test cases for MOEAD.

* Use sort_index for sorting that is supported for both armadillo and bandicoot.

* Revert changes to Parallel SGD optimizer, no benefit to upload to the GPU.

* Only reset if we haven't reached the end of the iterations.

* Use accu to the the sum of the vector.

* Use clamp instead of min, for a more efficient computation.

* Add missing data type.

* Use ENS_HAVE_COOT instead of USE_COOT and automatically detect when Bandicoot has already been included.

* Fix incorrect initialization list.

* Remove unnecessary GetFillType<MatType>::zeros, which does not work for sparse matrices.

* Clean up the last few proxy functions.

* Refactor problems to avoid adding extra template parameters.

* NSGA2: introduce a new Optimize function that takes the front and the paretoSet, so we can avoid introducing new template parameters.

* MOEAD: introduce a new Optimize function that takes the front and the paretoSet, so we can avoid introducing new template parameters.

* Remove unnecessary lr matrix.

* Minor cleanups for SA.

* Clean up member initialization for PSO.

* Update documentation for multi-objective optimizers.

* Update API for Snapshots().

* Keep the MOEAD front indices on the CPU, to be more efficient.

* Remove class template parameter for the bound parameter, to make the optimizer class easier.

* Use the correct location for the bandicoot wrapper.

* No need to set the backend for testing, we will test whatever was available during compilation.

* Use the correct def to enable the bandicoot backend.

* use the correct name for the def.

* Fixes so that Bandicoot actually gets detected and compiled in correctly.

* Oops, fix merge.

* Add tolerances for Bandicoot matrices.

* Fix missing typedef.

* Fix some failing tests.

* Fix minor compilation issues.

* Fix compilation errors and warnings.

* Minor issues discovered in self-review.

* Oops, fix missing type.

* Reduce batch size to try and prevent NaNs.

---------

Co-authored-by: Marcus Edel <marcus@urgs.org>
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2025-09-07 07:38:32 -04:00
conradsnicta 8579b392b2 use .index_min() instead of the deprecated form .min(index) (#436) 2025-09-03 09:37:50 -04:00
Ryan Curtin 198525ca7a Overhaul documentation for callbacks. 2025-08-31 20:31:32 -04:00
Marcus EdelandRyan Curtin b77cf519c1 Bandicoot intergration (#425)
* Add CMake modules to find Bandicoot libraries and includes.

* Add initial GitHub Actions to build against Bandicoot and run the tests.

* 1. Add type proxies to select the right type based on the used backend and datatype.
2. Update IQN optimizer to build against Armadillo and Bandicoot.
3. Update IQN test cases to test against Armadillo and Bandicoot backend.
4. Update logisitic regression function, to support Armadillo and Bandicoot.

* Refactor CNE optimizer and tests cases to work with Armadillo and Bandicoot datatypes.

* Refactor Katyusha optimizer and tests cases to work with Armadillo and Bandicoot datatypes.

* Refactor optimizers to work with Armadillo and Bandicoot datatypes:

1. AdaBelief
2. AdaBound
3. AMSBound
4. AdaGrad
5. Adam
6. Adamax
7. AMSGrad
8. Nadam
9. Nadamax
10. OptimisticAdam
11. Eve
12. FTML
13. LBFGS
14. Lookahead
15. Padam
16. QHAdam
17. SMORMS3
18. SpaleraSGD
19. SPSA
20. SWATS
21. WNGrad
22. Yogi
23. MomentumSGD
24. NesterovMomentum
25. QuasiHyperbolic
26. SGD
27. SGDR
28. SnapshotSGDR
29. CNE
30. CMAES
31. ActiveCMAES
32. PSO
33. LBestPSO
34. MOEAD
35. AugmentedLagrangian

* Refactor optimizers to work with Armadillo and Bandicoot datatypes:
1. AdaDelta
2. AdaSqrt
3. BigBatchSGD
4. CoordinateDescent (CD)
5. GradientDescent
6. GridSearch
7. NSGA2
8. ParallelSGD

* Refactor optimizers to work with Armadillo and Bandicoot datatypes:
1. DifferentialEvolution (DE)
2. RMSProp
3. SimulatedAnnealing (SA)
4. SARAH
5. SARAH+
6. SVRG
7. BarzilaiBorweinDecay
8. DemonAdam
9. DemonSGD

* Use inverse transform sampling to replicating arma::randg sampling since randg is not supported in Bandicoot.

* Apply suggestions from code review by @rcurtin.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Group test cases and fix indentations.

* Update optimizer reference styling.

* Add option to build against Bandicoot (by default we build against Bandicoot if found).

* Mention AugLagrangianType constructor in the optimizer documentation, that allows to use a different datatype i.e. arma::fvec or coot::fvec.

* Use data type specific element type for the bounds parameter.

* Make sure we follow the max character line limitation from the style guide.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Remove backports for outdates armadillo versions.

* Indentation fixes.

* Indentation fixes.

* Use the correct data type during testing.

* Do not use the namespace twice.

* Test against CPU/OpenCL/CUDA backend.

* Use the correct test case type.

* We don't need Bandicoot when we test against Armadillo (CPU) backend.

* Use the correct minimal Bandicoot version.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Comment on the added proxies.

* Merge arma and coot traits into a single file.

* Apply suggestions from code review.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Refactor ForwardType into two different structures to avoid std::conditional.

* Remove unused proxies.

* Revert CategoryType to arma::Row<size_t> since that should always stay on the CPU.

* Add convenience IsMatrixType<> struct.

* Individually accessed vector, so keep vector on the CPU, for faster access.

* Apply suggestions from code review

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Kepp the indicies on the CPU.

* Apply suggestions from code review.

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Remove outdated test case.

* Use initialization list for the velocity vector.

* Apply suggestions from code review

Co-authored-by: Ryan Curtin <ryan@ratml.org>

* Readd removed test cases for MOEAD.

* Use sort_index for sorting that is supported for both armadillo and bandicoot.

* Revert changes to Parallel SGD optimizer, no benefit to upload to the GPU.

* Only reset if we haven't reached the end of the iterations.

* Use accu to the the sum of the vector.

* Use clamp instead of min, for a more efficient computation.

* Add missing data type.

* Use ENS_HAVE_COOT instead of USE_COOT and automatically detect when Bandicoot has already been included.

* Fix incorrect initialization list.

* Remove unnecessary GetFillType<MatType>::zeros, which does not work for sparse matrices.

* Clean up the last few proxy functions.

* Refactor problems to avoid adding extra template parameters.

* NSGA2: introduce a new Optimize function that takes the front and the paretoSet, so we can avoid introducing new template parameters.

* MOEAD: introduce a new Optimize function that takes the front and the paretoSet, so we can avoid introducing new template parameters.

* Remove unnecessary lr matrix.

* Minor cleanups for SA.

* Clean up member initialization for PSO.

* Update documentation for multi-objective optimizers.

* Update API for Snapshots().

* Keep the MOEAD front indices on the CPU, to be more efficient.

* Remove class template parameter for the bound parameter, to make the optimizer class easier.

* Use the correct location for the bandicoot wrapper.

* No need to set the backend for testing, we will test whatever was available during compilation.

* Use the correct def to enable the bandicoot backend.

* use the correct name for the def.

* Fixes so that Bandicoot actually gets detected and compiled in correctly.

* Oops, fix merge.

* Oops, don't forget to resize the responses.

* Add new Optimize(...) method to AGEMOEA that takes the front and pareto set as input.

---------

Co-authored-by: Ryan Curtin <ryan@ratml.org>
2025-08-31 13:42:57 -04:00
Ryan Curtin d840de83c0 Add new block for next release to HISTORY.md. 2025-05-02 08:55:13 -04:00
Ryan Curtin 8ec25593b8 Update and release version 2.22.2. 2025-05-02 08:55:13 -04:00
conradsnicta 64ad05df10 update HISTORY.md (#424)
add #422 to HISTORY.md
2025-05-01 09:39:30 -04:00
Dirk Eddelbuettel ea9c291bed Set cmake version range to 3.5...4.0 (#422) 2025-04-30 03:16:31 +02:00
conradsnicta 78680bc819 increase tolerance (#420) 2025-02-26 13:45:24 +01:00
conradsnicta 7e81ed37b9 fix include (#419) 2025-02-26 13:44:19 +01:00
Ryan Curtin 7791091f2d Don't reset the objective estimate on the last iteration (#417) 2024-12-31 08:08:19 +01:00
James J Balamuta b0e33483fb Merge pull request #416 from rcurtin/specify-minimum-arma-versio
List minimum Armadillo version in README
2024-12-12 11:21:22 -08:00
Ryan CurtinandDirk Eddelbuettel 6f5f7467b4 Update README.md
Co-authored-by: Dirk Eddelbuettel <edd@debian.org>
2024-12-11 09:08:57 -05:00
Ryan Curtin e961819f33 List minimum Armadillo version in README. 2024-12-11 08:32:01 -05:00
Ryan Curtin 55ee7d5d7d Merge pull request #415 from rcurtin/release-2.22.1
Release version 2.22.1: "E-Bike Excitement"
2024-12-02 19:26:37 -05:00
Ryan Curtin 0243c0b92d Add new block for next release to HISTORY.md. 2024-12-02 14:51:33 -05:00
Ryan Curtin 65e359a29f Update and release version 2.22.1. 2024-12-02 14:51:33 -05:00
Ryan Curtin fa58c90e0f Merge pull request #414 from rcurtin/update-history
Add an entry in the HISTORY for #413
2024-12-02 14:51:04 -05:00
Ryan Curtin 052f58e6bb Simplify compilation options to also work on gcc. 2024-12-02 13:28:28 -05:00
Ryan Curtin 18d383f503 Add an entry in the HISTORY for #413. 2024-12-02 09:18:47 -05:00
Ryan Curtin 92468ea52f Merge pull request #413 from coatless-mlpack/bugfix/unused-variables
Bugfix/unused variables
2024-11-30 17:32:27 -05:00
James J Balamuta c456e8e6f0 Correct deletion location 2024-11-30 06:43:53 +00:00
James J Balamuta 77c6b06172 Also incorporate the Wunused-private-field flag 2024-11-30 05:55:49 +00:00
James J Balamuta f0aa97c769 Add to compilation settings for non-MSVC -Wunused-variable 2024-11-30 05:53:46 +00:00
James J Balamuta fcafecc1f7 Remove unused variable declarations to quiet -Wunused-variable compiler error 2024-11-30 05:49:55 +00:00
Ryan Curtin 9406243954 Add new block for next release to HISTORY.md. 2024-11-29 13:51:28 -05:00
Ryan Curtin 2c163f0655 Update and release version 2.22.0. 2024-11-29 13:51:28 -05:00
beny-bee 6ac2f6b462 IPOP and BIPOP restart mechanisms (#403) 2024-11-29 03:25:55 +01:00
Dirk Eddelbuettel 7bff76651d Merge pull request #409 from mlpack/feature/arma_14.2.0_change
Armadillo 14.2.0 deprecation changes to (index) .min() and .max()
2024-11-20 14:58:03 -06:00
Dirk Eddelbuettel b19f75e6c8 Update HISTORY.md [ci skip] 2024-11-19 14:02:16 -06:00
Dirk Eddelbuettel 0211ca3840 Update one deprected .max(k) call to .index_max() 2024-11-18 08:17:10 -06:00
Dirk Eddelbuettel 2753047455 Update from deprecated .min(ind) to .index_min()
This is an Armadillo 14.2.0 change that creates a fair amount of compilation
noise for mlpack right now.

There are likely more changes to be made but this will check the mechanics.
2024-11-18 08:06:30 -06:00
Ryan Curtin 882f8782ce Merge pull request #408 from ErikDervishi03/change_doc
Update callbacks.md
2024-10-17 10:04:48 -04:00
Erik DervishiandRyan Curtin 1168dc5a4c Apply suggestions from code review
changes to maintain document consistency

Co-authored-by: Ryan Curtin <ryan@ratml.org>
2024-10-07 21:15:57 +02:00
Erik Dervishi c65e7780c1 Update callbacks.md
Refactored the gradient clipping section in the README to reduce repetition and improve clarity
2024-10-06 16:17:03 +02:00
Marcus Edel bd6a308a63 Merge pull request #407 from IWNMWE/errorFix
AGEMOEA for Convex Fronts.
2024-08-28 10:28:17 -04:00
Marcus Edel b2eb798d2a Merge pull request #402 from IWNMWE/maf
Adding the MAF Benchmark problems.
2024-08-28 10:27:16 -04:00
IWNMWE c0fb97a07e changed if condition 2024-08-16 23:50:18 +05:30
IWNMWE 4bf9b015b4 add try catch for arma::solve 2024-08-14 15:25:13 +05:30
IWNMWE a0020a10c2 added try catch for arma::solve 2024-08-14 01:25:25 +05:30
IWNMWE 2f5228fdac Added test to moea/d 2024-08-14 01:20:51 +05:30
IWNMWE fc79105580 return full population after optimization 2024-08-13 18:25:17 +05:30
IWNMWE 4ba00c1432 style and error fixes 2024-08-10 02:00:58 +05:30
IWNMWE dfd7cb57c7 style fixes 2024-08-09 02:05:56 +05:30
IWNMWE 3716747b21 agemoea fixes 2024-08-08 21:46:55 +05:30
IWNMWE 292398a312 remove cout 2024-08-05 01:44:29 +05:30
IWNMWE 6135f394ca MAF fixes 2024-08-05 01:43:34 +05:30
Satyam Shukla 4fdbf09df4 Merge branch 'mlpack:master' into maf 2024-08-04 14:33:02 +05:30
Marcus Edel 65e0d03339 Merge pull request #399 from IWNMWE/agemoea_igd
Adding AGEMOEA and IGD.
2024-07-30 14:40:17 -04:00
IWNMWE b87cb77fd9 Remove extra space 2024-07-29 00:19:35 +05:30
IWNMWE 1aea611154 Documentation changes 2024-07-29 00:16:45 +05:30
IWNMWE 6db63f3445 Documentation changes 2024-07-28 00:11:05 +05:30
IWNMWE b1b51d2e72 ZDT3 test population percentage checks 2024-07-27 11:16:34 +05:30
IWNMWE af14105947 increase re tries for zdt3 2024-07-27 07:13:24 +05:30
IWNMWE 77c6fe4302 Added colab link 2024-07-27 01:29:02 +05:30
IWNMWE e00e5d7059 Fixing MAF4 2024-07-27 01:21:23 +05:30
IWNMWE d9495a9425 Fixed nsga2 documentation 2024-07-26 23:58:53 +05:30
IWNMWE 762b14e60d style fixes 2024-07-26 23:54:21 +05:30
IWNMWE 6a7f048ac9 Added documentation and style fixes 2024-07-26 23:45:02 +05:30
Ryan Curtin 8bba50fa9b Add actions to replace mlpack-bot. (#405) 2024-07-26 16:49:04 +02:00
Satyam Shukla c5b6b61fef Update maf4_function.hpp 2024-07-24 22:34:25 +05:30
IWNMWE b4fb3d7f9e change file path 2024-07-24 22:29:10 +05:30
IWNMWE 4095b1f1de change in nsga2 documentation 2024-07-24 22:25:32 +05:30
IWNMWE e32ef1bda3 style fixes 2024-07-24 22:06:02 +05:30
Ryan Curtin 6b0a0031f1 Merge pull request #404 from mlpack/arma-version-bump
Bump minimum Armadillo version to 10.8
2024-07-14 16:18:41 -04:00
conradsnicta 51c950663c bump to armadillo 10.8.2 2024-07-12 17:48:55 +02:00
conradsnicta 32015aeef1 bump to armadillo 10.8.2 2024-07-12 17:47:03 +02:00
conradsnicta 3ef869bf34 remove workaround for old versions of Armadillo 2024-07-11 04:49:19 +00:00
conradsnicta f8735180e0 remove workaround for old versions of Armadillo 2024-07-11 04:47:55 +00:00
conradsnicta 7d2dc1f7e6 remove workaround for old versions of Armadillo 2024-07-11 04:47:28 +00:00
conradsnicta 5f6a89e6f1 remove workaround for old versions of Armadillo 2024-07-11 04:46:55 +00:00
conradsnicta 2b01c9d43b remove workaround for old versions of Armadillo 2024-07-11 04:44:35 +00:00
conradsnicta 3ab90b37b8 remove workaround for old versions of Armadillo 2024-07-11 04:44:07 +00:00
conradsnicta 46443cc804 remove workaround for old versions of Armadillo 2024-07-11 04:43:36 +00:00
conradsnicta 54f755f711 remove workaround for old versions of Armadillo 2024-07-11 04:43:00 +00:00
conradsnicta dd1ff85707 remove workaround for old versions of Armadillo 2024-07-11 04:42:30 +00:00
conradsnicta 015c44bfc2 remove workaround for old versions of Armadillo 2024-07-11 04:41:50 +00:00
Satyam Shukla ad9e80f87b Updated ensmallen.hpp 2024-07-11 10:11:27 +05:30
conradsnicta b2bb7a19d9 remove workaround for old versions of Armadillo 2024-07-11 04:41:08 +00:00
conradsnicta c39b2f90d0 remove workaround for old versions of Armadillo 2024-07-11 04:40:17 +00:00
conradsnicta 32958a7fbd remove workaround for old versions of Armadillo 2024-07-11 04:39:17 +00:00
IWNMWE fb4149e308 Documentation changes 2024-07-11 00:43:57 +05:30
IWNMWE 4232de109c removed reference front methods 2024-07-11 00:40:05 +05:30
IWNMWE be5bc43feb added more tries to ZDT3 2024-07-10 22:31:50 +05:30
conradsnicta 770f11f217 update HISTORY.md 2024-07-10 01:58:28 +02:00
conradsnicta a9f306484a Bump minimum Armadillo version to 10.8 2024-07-10 01:45:40 +02:00
conradsnicta e1971f2b31 Bump minimum Armadillo version to 10.8.2 2024-07-10 01:42:42 +02:00
IWNMWE 4e90dcbfc1 Added documentation 2024-07-09 23:32:09 +05:30
IWNMWE ff72db847d update test params 2024-07-08 23:19:58 +05:30
IWNMWE 3e006e8efb error fix 2024-07-08 20:27:25 +05:30
IWNMWE c94232d50b Error Fixes 2024-07-07 00:02:59 +05:30
IWNMWE 746bf3ae7c error fixes 2024-07-06 00:52:44 +05:30
IWNMWE ab082be5af added includes 2024-07-05 23:47:25 +05:30
IWNMWE 3bdfc75bdb Reviewed changes 2024-07-05 22:27:55 +05:30
IWNMWE 94b93a28cc added maf2 2024-07-04 01:23:06 +05:30
IWNMWE 2c909bb584 resolved clamp error 2024-07-04 00:03:54 +05:30
IWNMWE 1bee1cc98d style fixes 2024-07-03 01:24:48 +05:30
IWNMWE c0bae8c5fd style changes 2024-07-03 01:16:29 +05:30
IWNMWE 1466ee58a8 Returns final paretoSet 2024-06-29 17:12:17 +05:30
IWNMWE 7ec24a3735 removed comments 2024-06-26 10:17:25 +05:30
IWNMWE 9f25ae217c Added agemoea_test 2024-06-26 00:30:14 +05:30
Satyam Shukla 9d935eb5eb Merge branch 'mlpack:master' into maf 2024-06-22 19:35:53 +05:30
IWNMWE b8af5cbeea added maf problems 2024-06-22 18:48:01 +05:30
Marcus Edel 1fde38ad86 Merge pull request #397 from IWNMWE/dtlz
Implementation of DTLZ problems.
2024-06-21 09:49:00 -04:00
IWNMWE 888b3d0d1d remove wrong size. 2024-06-17 20:20:19 +05:30
IWNMWE 998c88db13 Reviewed changes. 2024-06-17 20:16:20 +05:30
IWNMWE 8969f73474 bounds on c1 and c2 added 2024-06-16 16:22:38 +05:30
IWNMWE ce6a64c233 error fixes 2024-06-15 23:07:47 +05:30
IWNMWE 5e44e557aa fixed typos 2024-06-11 22:12:25 +05:30
IWNMWE 21b084c0fa Added Survival Score and optimization loop 2024-06-11 22:08:34 +05:30
Ryan Curtin b0879a1b4c Merge pull request #400 from mlpack/cpp14
update to C++14 standard
2024-06-08 15:18:07 -06:00
conradsnicta 6796e42e6c update HISTORY.md 2024-06-08 15:00:25 +02:00
conradsnicta c73a2bf77c use 'static constexpr bool' instead of 'static const bool' 2024-06-08 14:17:07 +02:00
conradsnicta 1d5fcacd9f ensure C++14 mode is enabled 2024-06-08 14:08:16 +02:00
conradsnicta 8933912046 remove no longer needed ens_deprecated macro 2024-06-08 14:02:14 +02:00
conradsnicta 798651124a use C++14 [[deprecated(...)]] instead of macro 2024-06-08 13:58:09 +02:00
conradsnicta cb82b5f854 avoid deprecation warnings from CMake 2024-06-08 11:44:04 +02:00
conradsnicta a9e3d71e36 update requirement to C++14 and remove outdated info 2024-06-08 11:41:55 +02:00
conradsnicta a25eb51836 use C++14 standard 2024-06-08 11:34:49 +02:00
IWNMWE 5622db0c5f polynomial mutation and error fixes 2024-06-02 11:26:23 +05:30
IWNMWE 745f741d8b added SBX and normalization 2024-06-02 00:38:32 +05:30
IWNMWE de8cbe5462 Initialized agemoea and igd 2024-05-31 22:20:58 +05:30
IWNMWE cbc6ce1970 style and comments 2024-04-24 19:13:22 +05:30
Marcus Edel bfa048bf9b Merge pull request #398 from mlpack/conradsnicta-patch-1
catch2 workaround for recent issue under macOS.
2024-03-26 00:00:36 -04:00
conradsnicta 3b9b278a2d workaround from upstream catch2 repo 2024-03-23 07:28:06 +01:00
IWNMWE 2d6f177f86 style fixes 2024-03-16 22:06:02 +05:30
IWNMWE 55ca12e64d added getObjectives for dtlz 2024-03-16 21:55:02 +05:30
IWNMWE c5f67f34e8 dltz7 fix 2024-03-16 13:43:53 +05:30
IWNMWE 586660885c fixed function errors 2024-03-15 00:21:38 +05:30
IWNMWE 546d0bef0b added dtlz 2024-03-13 15:03:48 +05:30
Ryan Curtin cd7fb34136 Add new block for next release to HISTORY.md. 2024-02-16 02:33:12 +01:00
Ryan Curtin 9d8dfb2597 Update and release version 2.21.1. 2024-02-16 02:33:12 +01:00
conradsnicta 13f66d01a8 ensure the tests are built with optimisation enabled (#394) 2024-02-15 02:20:22 +00:00
Ryan Curtin 0c64b0805c Merge pull request #392 from rcurtin/lbfgs-fix-negative-scaling-factors
Fix negative scaling factors for L-BFGS
2024-02-13 09:21:49 -05:00
Ryan Curtin 295fa0ae91 Update HISTORY. 2024-02-09 18:02:11 -05:00
Ryan Curtin acaa4de807 Fix instability in L-BFGS scaling factor computation.
When the norm of the gradient gets very small (but nonzero), we can end up with
very large scaling factors.  To avoid this, we now use a tolerance before
dividing.  In addition, this tolerance handles when the computed norm is
negative (which can happen due to precision issues).
2024-02-09 17:53:18 -05:00
Ryan Curtin d4f676b869 Don't print coordinates in verbose mode (they could be huge!). 2024-02-09 17:53:18 -05:00
Ryan Curtin bd0effce10 Fix compilation warnings for unused variables. 2024-02-09 17:53:18 -05:00
James J Balamuta 5bb9fac6cf Merge pull request #387 from rcurtin/release-2.21.0
Release version 2.21.0: "Bent Antenna"
2023-11-27 13:16:42 -07:00
Ryan Curtin a2b063a9da Add new block for next release to HISTORY.md. 2023-11-27 09:03:19 -05:00
Ryan Curtin d3df6dbf32 Update and release version 2.21.0. 2023-11-27 09:03:19 -05:00
Ryan Curtin 7ade3e9612 Merge pull request #384 from rcurtin/callbacks-use-results
Fix some callbacks that ignored return values
2023-11-26 09:11:22 -05:00
Ryan Curtin cbc90d8724 Merge pull request #385 from rcurtin/report-newline
Add a newline after printing coordinates to `Report` callback
2023-11-24 11:25:16 -05:00
Ryan Curtin d361b86b7d Another guess at MSBuild.exe location. 2023-11-24 10:36:09 -05:00
Ryan Curtin 666b47db4d Upgrade Visual Studio images to try and fix Windows build. 2023-11-24 09:17:19 -05:00
Ryan Curtin 7c8491fa9b Fix static code analysis issue for static functions. 2023-11-23 23:49:57 -05:00
Ryan Curtin e21a3c57ae Use constexpr instead of const. 2023-11-23 13:00:10 -05:00
Ryan Curtin 3160cba40d Update HISTORY.md. 2023-11-22 13:17:05 -05:00
Ryan Curtin 689437ccd4 Add a newline after printing coordinates. 2023-11-22 13:15:37 -05:00
Ryan Curtin 74beb984f2 Adapt all callbacks to return bools instead of void. 2023-11-15 10:30:35 -05:00
Ryan Curtin 05b1f284a5 Fix callbacks to respect the return values of Evaluate() and other calls. 2023-11-15 10:30:16 -05:00
Ryan Curtin f73995b641 Fix callback return values and clarify docs (#383) 2023-11-15 02:38:05 +01:00
Marcus Edel 226326314d Merge pull request #382 from DiscreteLogarithm/patch-1
Fixed bool form signature of several callback methods.
2023-11-10 14:59:56 -05:00
Ryan Curtin 354d21aa72 Update HISTORY.md. 2023-11-09 08:57:15 -05:00
Morteza Jalalvand 387d73e72f Fixed bool form signature of several callback methods 2023-11-06 11:43:57 +03:30
James J Balamuta 868793bddb Merge pull request #380 from rcurtin/release-2.20.0
Release version 2.20.0: "Stripped Bolt Head"
2023-10-04 16:39:47 -07:00
Ryan Curtin c46cebdbbb Add new block for next release to HISTORY.md. 2023-10-02 10:32:15 -04:00
Ryan Curtin 1aee913b81 Update and release version 2.20.0. 2023-10-02 10:32:15 -04:00
James J Balamuta 42cdf42e3b Merge pull request #379 from rcurtin/scd-to-cd
Rename `SCD` to `CD`
2023-09-28 22:18:04 -07:00
Ryan Curtin 5bc145b0f8 Update HISTORY. 2023-09-25 22:00:08 -04:00
Ryan Curtin ab828190db Update tests. 2023-09-25 21:57:09 -04:00
Ryan Curtin fa8b001eeb Update documentation for SCD -> CD change. 2023-09-25 21:57:00 -04:00
Ryan Curtin 5f17568404 Rename SCD to CD. 2023-09-25 21:54:52 -04:00
Marcus Edel b97c8b08e2 Merge pull request #376 from rcurtin/fix-history
Remove duplicate HISTORY.md entry.
2023-09-01 16:51:06 -04:00
Ryan Curtin 7d2785f884 Remove duplicate HISTORY.md entry. 2023-09-01 14:29:23 -04:00
Marcus Edel 1cfe984104 Merge pull request #367 from SuvarshaChennareddy/active-cmaes
Implementation of Active CMAES.
2023-08-21 19:10:42 -04:00
Marcus Edel 43c0cb38bd Use the correct population size. 2023-08-21 13:38:23 -04:00
Marcus Edel 9f71c1cb16 No need to depreciate the CMAES constructor. 2023-08-21 10:40:46 -04:00
Suvarsha Chennareddy e8284423f1 add documentation 2023-08-18 20:02:38 +05:30
Marcus Edel 7bdf982245 Increase number of steps to make the tests more stable. 2023-08-17 13:24:48 -04:00
Marcus Edel 25ef5ff01a Update HISTORY.md. 2023-08-16 16:04:56 -04:00
Marcus Edel a0a9400dfe Make the test more stable by testing multiple times before reporting a failure. 2023-08-16 16:03:06 -04:00
Marcus Edel e4a843b9ee Use the correct element type and style fixes. 2023-08-16 16:01:49 -04:00
Marcus Edel c92b48e8a8 Indentation fixes. 2023-08-16 16:00:31 -04:00
Marcus Edel 4f6e31226f Use the correct element type and style fixes. 2023-08-16 15:58:42 -04:00
Marcus Edel 5a145e8a4f Fix style issues and keep the constructor. 2023-08-16 15:57:37 -04:00
Suvarsha Chennareddy caaa9263f4 move NotEmptyTransformation to its own file 2023-08-07 22:17:30 +05:30
Marcus Edel 52e449150b Merge pull request #351 from SuvarshaChennareddy/cmaes-fix
An attempt to fix the current CMAES inconsistencies.
2023-07-24 00:01:18 -04:00
Suvarsha Chennareddy ee7b695d4c add patience and update termination conditions 2023-07-12 23:44:44 +05:30
Suvarsha Chennareddy 8ea9d06efc add patience and update termination conditions 2023-07-12 23:35:49 +05:30
Suvarsha Chennareddy 8a4e2e6c6c update population size used for CMAESLogisticRegressionFMatTest 2023-07-12 20:26:28 +05:30
Suvarsha Chennareddy c8719fb7ad minor comment fix in callbacks_test.cpp 2023-07-10 21:02:59 +05:30
SuvarshaChennareddy b791390527 Merge branch 'master' into cmaes-fix 2023-07-10 20:33:41 +05:30
SuvarshaChennareddy 3eafed5864 Merge branch 'master' into active-cmaes 2023-07-10 20:32:03 +05:30
Suvarsha Chennareddy 0785776adc add missing comment 2023-07-10 20:02:07 +05:30
Suvarsha Chennareddy a6fb39127b add missing comment 2023-07-10 19:59:39 +05:30
conradsnicta b702f6db50 upgrade to Catch v2.13.10 (#370) 2023-07-09 05:52:25 +02:00
Marcus Edel f3b4d5a534 Save some memory for the Windows build. (#371) 2023-07-09 05:51:18 +02:00
conradsnicta 626614127c undefine conflicting As macro (#369) 2023-06-23 16:43:38 +02:00
conradsnicta 2e0ab12a97 LBFGS: avoid generation of NaNs, and add checks for finite values (#368) 2023-06-23 02:31:55 +02:00
Suvarsha Chennareddy ef6396641f Update hyperparameters used in tests 2023-06-14 16:29:40 +05:30
Suvarsha Chennareddy 5623d04a82 Update hyperparameters used in tests 2023-06-14 16:26:59 +05:30
Suvarsha Chennareddy 37b342c83c update comments 2023-06-14 00:27:25 +05:30
Suvarsha Chennareddy 0cc8d0c6f5 update tests 2023-06-13 13:43:01 +05:30
Suvarsha Chennareddy 1c4a4db687 update tests 2023-06-13 13:39:18 +05:30
Suvarsha Chennareddy a0843dc2f4 update active cmaes tests 2023-06-13 00:20:36 +05:30
Suvarsha Chennareddy b518702322 Merge branch 'active-cmaes' of https://github.com/SuvarshaChennareddy/ensmallen into active-cmaes 2023-06-13 00:07:44 +05:30
Suvarsha Chennareddy 9ae0e6d979 update cmaes tests 2023-06-13 00:06:18 +05:30
SuvarshaChennareddy c937e335be remove empty line 2023-06-13 00:00:10 +05:30
Suvarsha Chennareddy b49b03dbb7 remove empty line 2023-06-12 23:55:12 +05:30
Suvarsha Chennareddy 26c1a233f0 update tests 2023-06-12 23:49:46 +05:30
Suvarsha Chennareddy 3c9bcfb947 fix implementation error and update tests 2023-06-12 23:47:00 +05:30
Suvarsha Chennareddy 52c4d63b89 add tests 2023-06-12 19:03:32 +05:30
Suvarsha Chennareddy e2e9065cee add include gaurds for transformation policies 2023-06-09 20:14:31 +05:30
Suvarsha Chennareddy 8baccf8990 add implementation of active cmaes 2023-06-09 20:02:13 +05:30
James J Balamuta 4e36f115c7 Merge pull request #366 from mlpack/patch-1
add short list of example optimisers
2023-06-06 22:09:17 -07:00
conradsnicta bc0b4abdbd add short list of example optimisers
Add example optimisers to increase relevant keywords.

This increases the likelihood of ensmallen being found via web search (eg. users typing specific keywords into google).
2023-06-07 01:57:09 +02:00
Suvarsha Chennareddy 716cea6a77 fix bug and modify tests 2023-06-01 00:49:14 +05:30
Ryan Curtin 46565e7e0a Compilation fixes for tests. 2023-05-29 11:54:21 -04:00
Ryan Curtin c88e0c3395 Remove some more tab characters I found... 2023-05-29 11:54:04 -04:00
Ryan Curtin d42f98546d Tiny style fix for name of function: initialStepSize -> InitialStepSize. 2023-05-29 11:53:42 -04:00
Marcus Edel ac102066e8 Merge branch 'master' into cmaes-fix 2023-05-24 09:14:02 -04:00
Suvarsha Chennareddy 5a00748d1e update HISTORY.md 2023-04-28 00:55:39 +05:30
Suvarsha Chennareddy 43f5146af5 update documentation, add ens_deprecated, and fix errors 2023-04-28 00:48:25 +05:30
Ryan Curtin 27246082ac Fix CNE test tolerances (#360) 2023-02-26 13:29:07 +01:00
Ryan Curtin 13885d8775 Release version 2.19.1: "Eight Ball Deluxe" (#356) 2023-02-08 01:22:06 +01:00
Ryan Curtin 8194ad9cd3 Merge pull request #354 from rcurtin/schaffer-n1-dims
Fix dimensionality of bounds for uses of SchafferFunctionN1
2023-01-28 14:25:03 -05:00
Ryan Curtin 91df4c54e9 Fix dimensionality of bounds for uses of SchafferFunctionN1. 2023-01-26 18:48:15 -05:00
Suvarsha Chennareddy 21e2e89911 remove unused code 2023-01-19 17:46:01 +05:30
Suvarsha Chennareddy 4a42820176 change test name 2023-01-17 01:05:06 +05:30
Suvarsha Chennareddy 39feebba14 fix style, add deprecated constructor, and update tests 2023-01-17 00:52:58 +05:30
Suvarsha Chennareddy f28fd18004 update tests 2023-01-02 12:20:56 +05:30
Suvarsha Chennareddy 670ebb2e12 attempt to fix current cmaes inconsistencies 2023-01-02 11:20:32 +05:30
conradsnicta adf2fc88dd avoid deprecation warnings in armadillo 11.2+ (#347)
avoid deprecation warnings in armadillo 11.2+
2022-06-30 04:12:36 +00:00
Marcus Edel f974ea0945 Merge pull request #343 from zoq/release-2.19.0
Release version 2.19.0: "Eight Ball Deluxe"
2022-04-08 21:10:49 -04:00
Marcus Edel a30c1a98b3 Add new block for next release to HISTORY.md. 2022-04-06 20:55:31 -04:00
Marcus Edel a5b57ba657 Update and release version 2.19.0. 2022-04-06 20:55:31 -04:00
conradsnicta 99147a88b1 bump check for min supported version of armadillo (#342)
followup to #318
2022-04-06 05:15:58 +02:00
Marcus Edel 1fd8d34e28 Merge pull request #211 from zoq/DemonSGD
DemonSGD & DemonAdam.
2022-04-05 20:47:19 -04:00
Marcus Edel a33dc777fc Merge branch 'master' into DemonSGD 2022-04-01 23:14:06 -04:00
Marcus Edel 394926879f Merge pull request #340 from rcurtin/iteration-in-update-policy
Move `iteration` member into UpdatePolicy classes.
2022-03-27 21:10:15 -04:00
Ryan Curtin 097024c656 Fix some dead store issues. 2022-03-25 18:21:39 -04:00
Ryan Curtin ab4481a387 Add a test to ensure that resetting the update policy works. 2022-03-25 17:56:45 -04:00
Ryan Curtin dbcc51062d Fix a couple PVS-Studio issues. 2022-03-24 21:06:23 -04:00
Ryan Curtin 4d9d060d15 Update HISTORY. 2022-03-23 18:55:49 -04:00
Ryan Curtin 9f5ab09db6 Move iteration member into UpdatePolicy classes. 2022-03-23 18:52:29 -04:00
Marcus Edel 5990bac4c6 Make demon test suite more stable. 2022-03-02 21:31:50 -05:00
Marcus Edel 69abe29952 Merge pull request #234 from zoq/ada_sqrt
AdaSqrt - Second-order Information in First-order Optimization Methods.
2022-02-28 20:48:28 -05:00
Marcus Edel 063f1eb364 Adjust CNE Himmelblau optimizer settings to make the test case more robust. 2022-02-27 22:01:19 -05:00
Marcus Edel 9929e1c81e Use smaller step size for the first iteration to stabilize the test suite. 2022-02-26 21:17:46 -05:00
Marcus Edel 8d68925e11 Use smaller step size for the first iteration to stabilize the test suite. 2022-02-24 22:06:58 -05:00
Marcus Edel df69ffb564 Unify header inclusion format. 2022-02-23 20:39:32 -05:00
Marcus Edel bb31a4455a Unify header inclusion format. 2022-02-22 21:54:06 -05:00
Marcus Edel 0dfa8fb929 Remove superfluous header. 2022-02-21 18:09:45 -05:00
Marcus Edel 69d6cf178c Unify header inclusion format. 2022-02-21 18:09:45 -05:00
Marcus EdelandConrad Sanderson 7776391f21 Use armadillo function to square the gradient.
Co-authored-by: Conrad Sanderson <conradsnicta@users.noreply.github.com>
2022-02-20 20:23:36 -05:00
Marcus Edel 3d31cc03ea Use the correct optimizer for the AdaSqrt test suite. 2022-02-19 13:23:02 -05:00
Marcus Edel 3486dd16b1 Update test suits to use updated test framework. 2022-02-18 21:52:19 -05:00
Marcus Edel be570d5ff9 Merge branch 'master' into ada_sqrt 2022-02-18 21:45:11 -05:00
Marcus EdelandRyan Curtin 3fe169e453 Correct test author.
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2022-02-18 21:44:27 -05:00
Marcus Edel 8afcc2e31f Merge pull request #233 from zoq/ada_belief
AdaBelief - Adapting Stepsizes by the Belief in Observed Gradients.
2022-02-18 19:42:51 -05:00
Marcus Edel 30f56ac990 Merge branch 'master' into ada_belief 2022-02-18 16:10:48 -05:00
Marcus Edel f401c9e95c Merge branch 'master' into DemonSGD 2022-02-17 22:24:45 -05:00
Marcus Edel f1b3cc6fd6 Merge pull request #232 from zoq/yogi
Yogi - Adaptive Methods for Nonconvex Optimization.
2022-02-17 21:41:42 -05:00
Marcus Edel d3686fa6a2 Merge branch 'master' into ada_sqrt 2022-02-17 20:38:56 -05:00
Marcus Edel 3bb5ea6df0 Merge branch 'master' into ada_belief 2022-02-17 20:30:48 -05:00
Marcus Edel 9176ceced3 Use armadillo function to get the square gradient. 2022-02-15 22:12:59 -05:00
Marcus Edel 3383542a4a Update test suits to use updated test framework. 2022-02-15 22:12:42 -05:00
Marcus Edel f5c25cc9aa Remove duplication from the history file. 2022-02-15 20:40:24 -05:00
Marcus Edel 1d584119d3 Merge branch 'master' into yogi 2022-02-15 20:39:11 -05:00
Marcus Edel ba981aad7a Update histroy - add Yogi optimizer. 2022-02-15 20:38:30 -05:00
Ryan Curtin a1a84c8fcb Add new block for next release to HISTORY.md. 2022-02-15 20:38:30 -05:00
Ryan Curtin 80d42cb643 Update and release version 2.18.2. 2022-02-15 20:38:30 -05:00
Marcus Edel 7bc1ee09ea Update test suits to use updated test framework. 2022-02-14 22:29:23 -05:00
Marcus Edel b9413f2bbf Add DemonSGD and DemonAdam optimizer documentation. 2022-02-14 22:27:20 -05:00
Marcus Edel 427d4b600e Merge branch 'master' into DemonSGD 2022-02-14 21:32:03 -05:00
Marcus Edel ec7d984120 Report the correct maximum number of iterations. 2022-02-14 21:30:13 -05:00
Marcus Edel c2ec446c3c Reference Demon typedefs. 2022-02-14 21:27:54 -05:00
Ryan Curtin a14b08f8d7 Add new block for next release to HISTORY.md. 2022-02-14 09:36:35 -05:00
Ryan Curtin 384912f38f Update and release version 2.18.2. 2022-02-14 09:36:35 -05:00
Marcus Edel 694bd1d049 Merge branch 'master' into yogi 2022-02-13 21:42:01 -05:00
Marcus Edel b153c95a5b Extend optimizer description. 2022-02-13 21:41:08 -05:00
Marcus Edel 6506d3a199 Remove unrelated paper from the reference section. 2022-02-13 21:36:40 -05:00
Marcus Edel f077ea57e5 Report the correct epoch time in seconds. (#337) 2022-02-12 05:27:13 +01:00
Conrad Sanderson f75b532d16 update Catch2 to 2.13.8 (#336) 2022-02-02 04:12:47 +01:00
Marcus Edel a652ca2ce5 Add new block for next release to HISTORY.md. 2021-11-20 11:32:46 -05:00
Marcus Edel bf42276c5a Update and release version 2.18.1. 2021-11-20 11:32:46 -05:00
Marcus Edel c35e78e489 SCD partial gradient test fix (#332) 2021-11-19 04:24:23 +01:00
Conrad Sanderson d811ff9738 avoid infinite loop for single-precision matrices (#331) 2021-11-17 15:01:01 +01:00
Marcus Edel 525dd6d961 Merge pull request #330 from zoq/sgd-test-break
Accelerate SGD test time by skipping additional trials.
2021-11-15 21:15:44 -05:00
Marcus Edel 025095a180 Accelerate SGD test time by skiping additional trials. 2021-11-12 23:11:41 -05:00
Marcus Edel c493c55e2e Add new block for next release to HISTORY.md. 2021-10-21 19:45:07 -04:00
Marcus Edel ac933c5dba Update and release version 2.18.0. 2021-10-21 19:45:07 -04:00
Conrad Sanderson ee3f0e1ea2 Merge pull request #327
Fix MOEAD test stability
2021-10-20 04:52:48 +02:00
Ryan CurtinandMarcus Edel 7a8c5ae685 Apply suggestions from code review
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-10-18 11:37:18 -04:00
Ryan Curtin bd285743b3 Some cleanup. 2021-10-18 10:29:23 -04:00
Ryan Curtin 23aab9f924 Factor out constant things from the loop. 2021-10-18 10:07:02 -04:00
Ryan Curtin df3a1cf347 Update HISTORY for #324 too. 2021-10-18 10:02:17 -04:00
Ryan Curtin f3b0224187 Update HISTORY. 2021-10-16 13:02:44 -04:00
Ryan Curtin d58258ff6c Fix MOEAD test stability. 2021-10-16 12:58:29 -04:00
Conrad Sanderson 99de75ce7c Merge pull request #325
Fix for C++20 constructor template argument deprecation
2021-10-14 11:19:23 +02:00
Eliane Briand 0f634056d0 Fix for C++20 constructor template argument deprecation 2021-10-13 15:03:33 +02:00
Conrad Sanderson 1db28cf195 Merge pull request #323
more approachable and impactful blurb to match description on website
2021-10-12 07:09:45 +02:00
Conrad Sanderson a04945ea8b simplify blurb to match description on website 2021-10-11 04:21:26 +02:00
Conrad Sanderson 0c1d98d11c Update Catch2 to 2.13.7 (#322) 2021-09-21 03:46:57 +02:00
Marcus Edel cf434a0ed7 Update history. 2021-09-19 12:39:10 -04:00
Marcus Edel 6ec037e7bb Update Catch2 to 2.13.7. 2021-09-19 12:35:54 -04:00
Conrad Sanderson 38e5c691e9 Revert "Add citation file"
This reverts commit 8f074a421a.
2021-09-07 03:04:21 +02:00
Marcus Edel 8f074a421a Add citation file. (#317) 2021-09-06 02:03:00 +02:00
Marcus Edel 1490193fb0 GradClipByValue callback (#315) 2021-08-31 06:53:03 +02:00
Marcus Edel 1208c62381 Bump minimum Armadillo version to 9.800. (#318)
* Bump minimum Armadillo version to 9.800.
* Update history.
* Download armadillo from the offical source (sourceforge).
2021-08-30 03:58:17 +02:00
Ryan Curtin f90ccda31d Merge pull request #316 from mlpack/conradsnicta-citation-update
update citation to JMLR paper
2021-08-15 15:08:10 -04:00
Conrad Sanderson bf77ef6bde update citation to JMLR paper 2021-08-14 05:21:14 +02:00
Conrad Sanderson 8b4cadf3ab Merge pull request #314 from zoq/travis-update
Update travis config to build the ensmallen tests
2021-08-09 04:51:48 +02:00
Conrad Sanderson c3f71b8099 Merge pull request #313 from zoq/test-build
The BUILD_TESTS option was replaced with 'make ensmallen_tests'.
2021-08-09 04:50:47 +02:00
Marcus Edel 9e8696bdb1 Do not fail the build on a warning. 2021-08-06 18:13:20 +02:00
Marcus Edel 122be35c0f Update travis config to build the ensmallen tests. 2021-08-06 02:58:39 +02:00
Marcus Edel b92e576f88 Update history. 2021-08-05 03:20:51 +02:00
Marcus Edel 7b0e36ec3b The BUILD_TESTS option was replaced with 'make ensmallen_tests'. 2021-08-04 05:09:46 +02:00
Ryan Curtin 237daa9c78 Merge pull request #310 from jonpsy/release-2.17.0
Release version 2.17.0: "Pachis Din Me Pesa Double"
2021-07-06 14:59:06 -04:00
Nanubala Gnana Sai c45abbdd9f Add new block for next release to HISTORY.md. 2021-07-06 16:09:25 +00:00
Nanubala Gnana Sai 9bd4bc2527 Update and release version 2.17.0. 2021-07-06 16:09:25 +00:00
Nanubala Gnana Sai 000d4c8488 Update HISTORY.md for release 2.17.0. 2021-07-06 16:08:50 +00:00
Conrad Sanderson b7aab43451 Merge pull request #299 from jonpsy/iterate-moead
Repair MOEAD
2021-07-05 14:43:36 +02:00
Marcus Edel ee8e1601bb Merge pull request #306 from jonpsy/nsga2-speed
Reduce LOC of NSGA-II.
2021-07-04 12:10:41 -04:00
Conrad Sanderson ec114f1904 Merge pull request #305 from mlpack/conradsnicta-init-fixes
various fixes for matrix initialisation
2021-07-02 05:34:37 +02:00
Conrad Sanderson 7582788d14 fix PR number 2021-07-02 05:32:20 +02:00
Nanubala Gnana SaiandMarcus Edel 3b78ab0d43 Update include/ensmallen_bits/nsga2/nsga2_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-07-01 22:13:17 +05:30
Nanubala Gnana Sai 3b4015c289 Update nsga2_impl.hpp
fix indent again
2021-07-01 21:57:09 +05:30
Nanubala Gnana Sai 0dd2b4c6fc Update nsga2_impl.hpp
fix indent
2021-07-01 21:56:23 +05:30
Nanubala Gnana Sai 0873d4a3b9 done changes. 2021-07-01 16:24:11 +00:00
Conrad Sanderson e5f2ca2553 update HISTORY.md 2021-07-01 13:50:39 +02:00
Nanubala Gnana Sai f4da19da12 resize => set_size 2021-07-01 04:51:57 +00:00
Conrad Sanderson 055ebfd3e9 use fill::none to avoid default zeroing 2021-07-01 05:45:17 +02:00
Conrad Sanderson 777fb6ac36 explicitly initialise to zeros 2021-07-01 05:38:54 +02:00
Conrad Sanderson d6ffdc59fa avoid zeroing twice 2021-07-01 05:31:53 +02:00
Conrad Sanderson ba167cefa0 use fill::none to avoid default zeroing 2021-07-01 05:21:18 +02:00
Nanubala Gnana Sai 6d4914ab72 remove unneccessary evaluate() call 2021-07-01 01:39:00 +00:00
Conrad Sanderson 44bb57ceb6 Merge pull request #304 from rcurtin/readd-deprecated-front
Add Front() back into the NSGA2 class.
2021-06-30 04:00:30 +02:00
Ryan Curtin 83bc231e1b Remove unused variables. 2021-06-28 13:38:49 -04:00
Ryan Curtin 9906c22d62 Use 3.x as the version number. 2021-06-28 11:51:24 -04:00
Ryan Curtin 4faea9791a Merge pull request #303 from mlpack/conradsnicta-dont-build-tests-by-default
don't build tests by default
2021-06-28 11:46:31 -04:00
Ryan Curtin 4c0accde96 Add Front() back into the NSGA2 class. 2021-06-28 11:43:15 -04:00
Conrad Sanderson 7a870142d0 update HISTORY.md 2021-06-27 07:06:24 +02:00
Conrad Sanderson a622a767de fix minor typo 2021-06-27 06:59:09 +02:00
Conrad Sanderson 735ae35521 building tests is now optional
- state that building tests is now optional
- show how to build and run the tests
2021-06-27 06:53:04 +02:00
Conrad Sanderson f51bfd71e7 don't build ensmallen_tests as part of make all 2021-06-27 06:43:53 +02:00
Nanubala Gnana Sai 9d55d24b3c Update pbi_decomposition.hpp
Use default arma::norm p value.
2021-06-26 01:35:02 +05:30
Marcus Edel 176aea6ef9 Merge pull request #298 from jonpsy/zdt_fix
ZDT4 Evaluate Fix.
2021-06-25 14:54:12 -04:00
jonpsy 9cad4a5d24 L2 norm not L1. 2021-06-25 20:09:49 +05:30
Marcus Edel 54c2de48e9 Merge branch 'master' into ada_belief 2021-06-24 20:27:34 -04:00
Marcus Edel ea7fe09913 Use updated update strategy from the latest paper release. 2021-06-25 02:26:26 +02:00
Ryan Curtin e6406a5c9a Merge pull request #300 from mlpack/README-improved-install-instructions
improved installation and compilation instructions
2021-06-24 10:35:40 -04:00
Conrad Sanderson a0137b07bb adjust line lengths to 80 chars max
this is to make editing on phones easier :)
2021-06-24 15:43:25 +02:00
Conrad Sanderson af145271ba Merge branch 'master' into README-improved-install-instructions 2021-06-24 15:34:55 +02:00
Conrad Sanderson c0b361d88a fix merge clashes 2021-06-24 15:31:51 +02:00
Ryan Curtin 14e1717b8d Merge pull request #302 from jonpsy/history
Modify HISTORY.md release history
2021-06-24 09:06:50 -04:00
jonpsy 0a53dcf7a6 Fix main Evaluate() 2021-06-24 09:49:54 +05:30
jonpsy 3921d34db0 Move up for next release. 2021-06-24 09:04:11 +05:30
Marcus Edel 9232017bd5 Merge pull request #296 from jonpsy/dirichlet
Add Dirichlet Weight Initialization.
2021-06-23 15:54:18 -04:00
Nanubala Gnana Sai 9835cd1149 Update optimizers.md 2021-06-24 00:11:03 +05:30
Nanubala Gnana Sai 96006fa6b2 Merge branch 'master' into dirichlet 2021-06-24 00:08:45 +05:30
Marcus Edel 86cf8f3edb Merge pull request #295 from jonpsy/uniform
Add Das-Dennis weight initialization method.
2021-06-23 14:23:59 -04:00
Conrad Sanderson 0678b0518a change "automatic" to "cmake based"
clarify wording, as the cmake installation isn't really automatic (still need to type commands)
2021-06-23 16:44:31 +02:00
Conrad Sanderson bb18d79413 automatic installation before manual installation
* place automatic installation instructions before manual installation instructions
* adapt text to improve flow
* fix bug in path for cmake non-root installation
2021-06-23 16:35:30 +02:00
NanuSai 7ce177996a frontIndices should be a vector. 2021-06-23 17:54:31 +05:30
jonpsy 8ac18870ef Add source of GFG code. 2021-06-23 14:39:27 +05:30
Conrad Sanderson 3c1bad105c update HISTORY.md 2021-06-23 04:47:20 +02:00
Conrad Sanderson 30bf5d2a67 improved installation and compilation instructions
Improved installation and compilation instructions:
* show how to install manually (in addition to cmake)
* show how to install with and without root access
* added example compilation commands, using standard and non-standard installation directories
2021-06-23 04:06:59 +02:00
jonpsy 5b8fcdf2f9 Set iterate after optimization ends. 2021-06-22 22:41:49 +05:30
jonpsy c585d2e377 HISTORY change isn't required. 2021-06-22 22:21:06 +05:30
jonpsy f108cc51ca history added. 2021-06-22 22:14:12 +05:30
jonpsy edb7e15229 truncatedCoords should be MatType. 2021-06-22 22:11:00 +05:30
Nanubala Gnana SaiandMarcus Edel bb893f683c Update include/ensmallen_bits/moead/weight_init_policies/uniform_init.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-22 00:33:48 +05:30
NanuSai b9b011ea01 Test against ZDT3
Add DirichletMOEAD
2021-06-19 01:03:30 +05:30
NanuSai 3268605bd7 Merge branch 'uniform' of https://github.com/jonpsy/ensmallen into uniform 2021-06-19 00:07:34 +05:30
NanuSai 239916188f add BBSMOEAD 2021-06-19 00:07:16 +05:30
Nanubala Gnana SaiandMarcus Edel 4f297c7abd Update include/ensmallen_bits/moead/weight_init_policies/uniform_init.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-18 23:52:22 +05:30
Nanubala Gnana SaiandMarcus Edel cb3dc29b09 Update include/ensmallen_bits/moead/weight_init_policies/uniform_init.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-18 21:13:34 +05:30
Nanubala Gnana SaiandMarcus Edel 50f1245e01 Update include/ensmallen_bits/moead/weight_init_policies/uniform_init.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-18 21:13:28 +05:30
Nanubala Gnana SaiandMarcus Edel 3c0b4673b4 Update include/ensmallen_bits/moead/weight_init_policies/uniform_init.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-18 21:13:22 +05:30
NanuSai cf38624cf5 added history 2021-06-18 19:34:35 +05:30
NanuSai fde96643fc added in optimizers.md
tests are passing
2021-06-18 19:32:14 +05:30
NanuSai ee1bb9760a Added dirichilet 2021-06-18 19:31:11 +05:30
NanuSai f9c5d7f3d6 revert to normal 2021-06-18 19:05:59 +05:30
NanuSai d98f82599b nit 2021-06-18 19:04:36 +05:30
NanuSai a8c664ece0 oldschool 2021-06-18 18:59:18 +05:30
NanuSai 7ebc527c09 minor indent fix 2021-06-18 18:42:18 +05:30
NanuSai dbfcf7e20f RDP test 2021-06-18 18:20:51 +05:30
NanuSai cf1a9e7e03 document in optimizers.md 2021-06-18 18:14:23 +05:30
NanuSai 501d486fa3 add history 2021-06-18 18:11:28 +05:30
NanuSai 275ded335d Add the braces 2021-06-18 18:09:54 +05:30
NanuSai d7be57a90b fix test params 2021-06-18 18:09:54 +05:30
NanuSai 96a1998ab6 efficient Binomial coefficient O(n) 2021-06-18 18:09:54 +05:30
NanuSai 60f92647a2 => Added uniform init
=> Use 300 instead of 150 population
=> DefaultMOEAD = MOEAD<Uniform, Tchebycheff>
2021-06-18 18:09:54 +05:30
Marcus Edel 0af8004ff9 Merge pull request #293 from jonpsy/moead+
Introduce Policy Methods for MOEA/D-DE.
2021-06-18 08:26:14 -04:00
NanuSai ff03b06848 restore cmake 2021-06-17 00:40:50 +05:30
NanuSai bd6cf8a116 Set bad threshold as of now. 2021-06-17 00:04:23 +05:30
NanuSai 18a4ca67e8 revert appveyor.yml 2021-06-16 23:59:07 +05:30
NanuSai a8b9018d83 use arma::diff 2021-06-16 23:52:31 +05:30
NanuSai 91ed336d0f remove stackoverflow link 2021-06-16 23:41:54 +05:30
NanuSai d66c3d0ca9 Merge branch 'moead+' of https://github.com/jonpsy/ensmallen into moead+ 2021-06-16 23:40:04 +05:30
NanuSai e3b865fcf0 revert moead test 2021-06-16 23:26:47 +05:30
Nanubala Gnana SaiandMarcus Edel e2b883dbda Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 23:24:16 +05:30
Nanubala Gnana SaiandMarcus Edel 955e7db555 Update include/ensmallen_bits/moead/weight_init_policies/bbs_init.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 22:35:18 +05:30
Nanubala Gnana SaiandMarcus Edel d7f2be2adb Update include/ensmallen_bits/moead/moead.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 22:35:06 +05:30
Nanubala Gnana SaiandMarcus Edel 79aa3f637a Update include/ensmallen_bits/moead/decomposition_policies/pbi_decomposition.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 22:34:56 +05:30
Nanubala Gnana SaiandMarcus Edel c1fbf16247 Update include/ensmallen_bits/moead/decomposition_policies/weighted_decomposition.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 22:34:46 +05:30
Nanubala Gnana SaiandMarcus Edel be9f8b6346 Update include/ensmallen_bits/moead/decomposition_policies/tchebycheff_decomposition.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 22:34:35 +05:30
Nanubala Gnana SaiandMarcus Edel 7d86a46a7b Update include/ensmallen_bits/moead/decomposition_policies/pbi_decomposition.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 22:34:26 +05:30
Nanubala Gnana Sai c79f593ff2 no need to build redundant tests 2021-06-16 15:39:39 +00:00
Nanubala Gnana Sai 40c9aaad7f Start RDP 2021-06-16 15:38:32 +00:00
NanuSai 23ff19074a minor fix 2021-06-16 11:53:04 +05:30
NanuSai 77bd44bed3 added template doc in optimizer.md 2021-06-16 11:16:11 +05:30
NanuSai d02d5e46aa extra doc for decomp policy 2021-06-16 10:55:02 +05:30
NanuSai 4997f550d2 add doc for bbs 2021-06-16 10:54:43 +05:30
NanuSai 8d81b422d2 indent 2021-06-16 10:47:06 +05:30
NanuSai 6e3e323654 doc decomposition policy 2021-06-16 10:42:54 +05:30
NanuSai de612d3d09 using DefaultMOEAD 2021-06-16 10:30:08 +05:30
NanuSai 9beaa5f0d4 InitPolicy, Generate const 2021-06-16 10:29:02 +05:30
Nanubala Gnana SaiandMarcus Edel 2c02c5f969 Update include/ensmallen_bits/moead/decomposition_policies/tchebycheff_decomposition.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 10:25:43 +05:30
Nanubala Gnana SaiandMarcus Edel 8952378a04 Update include/ensmallen_bits/moead/decomposition_policies/weighted_decomposition.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 10:25:37 +05:30
Nanubala Gnana SaiandMarcus Edel 4ec1f2e430 Update include/ensmallen_bits/moead/moead.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 10:25:30 +05:30
Nanubala Gnana SaiandMarcus Edel ade51c915b Update include/ensmallen_bits/moead/decomposition_policies/pbi_decomposition.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 10:25:23 +05:30
Nanubala Gnana SaiandMarcus Edel e092c4d262 Update include/ensmallen_bits/moead/moead.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 08:54:03 +05:30
Nanubala Gnana SaiandMarcus Edel 4692047af2 Update include/ensmallen_bits/moead/moead.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 08:53:44 +05:30
Nanubala Gnana SaiandMarcus Edel cfed986674 Update include/ensmallen_bits/moead/moead.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-16 08:53:26 +05:30
NanuSai 6e1164e952 remove uniform_init.hpp as of now 2021-06-16 08:43:58 +05:30
NanuSai ee41faad7d Tested against ZDT1 2021-06-16 08:24:02 +05:30
NanuSai 945e21abb5 history fix 2021-06-16 08:19:16 +05:30
NanuSai 6868e2ca94 restore all tests 2021-06-16 08:06:07 +05:30
NanuSai dbc24bbe4c moead doc fix 2021-06-16 08:06:07 +05:30
NanuSai 7505ecc6c7 fix decomposition policy docs 2021-06-16 08:06:07 +05:30
NanuSai e08c0c86c3 fix moead doc 2021-06-16 08:06:07 +05:30
NanuSai 9242279aba Re-configure tests 2021-06-16 08:06:07 +05:30
NanuSai d41c265202 Added Init policies
=> BayesianBootStrap : Working
=> Uniform : **NOT WORKING**
2021-06-16 08:06:07 +05:30
NanuSai 2749d31e7e Added decomposition policies
=> PBI
=> Tchebycheff
=> Weighted
2021-06-16 08:06:07 +05:30
NanuSai c66ee3a098 Make MOEA/D-DE policy based 2021-06-16 08:06:07 +05:30
NanuSai 9e6b3cad22 Fixed minor indent 2021-06-16 08:06:07 +05:30
Marcus Edel 3497f276f4 Merge pull request #269 from jonpsy/moead
Implement MOEA/D - DE.
2021-06-15 15:07:30 -04:00
Marcus Edel db90638fb3 Merge pull request #273 from jonpsy/ZDT
Adding ZDT Test Suite.
2021-06-14 12:30:44 -04:00
Nanubala Gnana SaiandMarcus Edel 973d4a7bcc Update include/ensmallen_bits/moead/moead.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-13 09:17:14 +05:30
Nanubala Gnana SaiandMarcus Edel da7683b770 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-13 09:17:04 +05:30
Nanubala Gnana SaiandMarcus Edel 5cd6bc8c29 Update tests/moead_test.cpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-13 09:16:56 +05:30
Nanubala Gnana SaiandMarcus Edel 40e44d4ede Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-13 09:16:47 +05:30
Nanubala Gnana SaiandMarcus Edel 7d794cb4bc Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-13 09:16:40 +05:30
Nanubala Gnana SaiandMarcus Edel a2a7ef166b Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-13 09:16:03 +05:30
Nanubala Gnana SaiandMarcus Edel b1fa440fda Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-13 09:15:39 +05:30
NanuSai e893dfd3ee weights are fixed bruh 2021-06-12 19:27:03 +05:30
NanuSai 53cae18ff6 fix default arg in ctor
fix callback test
2021-06-12 10:42:04 +05:30
NanuSai b7178fef0e Added epsilon to docs 2021-06-12 10:28:51 +05:30
NanuSai edd089a72b 2. => 2.0 2021-06-12 10:22:03 +05:30
NanuSai 60d19c48d7 if( 2021-06-12 10:21:19 +05:30
NanuSai 287bf825fa style fix 2021-06-12 10:20:49 +05:30
Nanubala Gnana Sai 5d9e73c139 Update moead_impl.hpp
Another day, another indent
2021-06-12 10:14:42 +05:30
NanuSai aa54aade4a BaseMatType 2021-06-12 10:02:13 +05:30
NanuSai b0c4c1b7b1 reorder tests 2021-06-12 09:59:26 +05:30
NanuSai 3267c42e4d RequireDenseFloatingCheck 2021-06-12 09:58:26 +05:30
NanuSai a646f7c136 re-ordered state variables 2021-06-12 09:55:29 +05:30
NanuSai 81e07bf455 minor style + numobjectives remvoed from opt 2021-06-12 09:37:21 +05:30
NanuSai 604209a0f7 added epsilon 2021-06-12 09:00:30 +05:30
NanuSai 1dadc1bbb0 delta indent fix 2021-06-12 08:46:53 +05:30
NanuSai 80fbf6c9e8 Mate fix 2021-06-12 08:46:30 +05:30
NanuSai ad9f355503 Mating more clear 2021-06-12 08:32:22 +05:30
Nanubala Gnana Sai 27404cdaad Update moead_impl.hpp 2021-06-12 00:25:33 +05:30
Nanubala Gnana Sai cf850f50d1 Update moead_impl.hpp
pick is const
2021-06-12 00:23:33 +05:30
Nanubala Gnana Sai 9d93405f5b Update moead_impl.hpp
delta should be generated per gene.
2021-06-12 00:23:02 +05:30
Nanubala Gnana SaiandMarcus Edel 4824e72f93 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-11 23:09:17 +05:30
Nanubala Gnana SaiandMarcus Edel 3404708708 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-11 23:09:09 +05:30
Nanubala Gnana SaiandMarcus Edel 4e4fa69b08 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-11 23:08:56 +05:30
Nanubala Gnana SaiandMarcus Edel 0828c7bfe4 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-11 23:08:45 +05:30
Nanubala Gnana SaiandMarcus Edel 7505121ee6 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-11 23:08:28 +05:30
Nanubala Gnana SaiandMarcus Edel bd2522667e Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-11 23:08:17 +05:30
NanuSai fb587225d4 fix error 2021-06-10 14:51:56 +05:30
NanuSai 6acbe14bfb minor style 2021-06-10 11:59:36 +05:30
Nanubala Gnana Sai 950e36c2d0 Update moead_impl.hpp
Indent fix
2021-06-10 11:57:40 +05:30
Nanubala Gnana Sai d2a396cde1 Update moead.hpp
Fix indent
2021-06-10 11:55:30 +05:30
NanuSai b5f49a00a2 Fix callbacks 2021-06-10 10:08:38 +05:30
NanuSai a068a6e40d Fix docs for optimizers.md 2021-06-10 09:53:25 +05:30
NanuSai 5adfba0bbd use distributionIndex again 2021-06-10 09:53:14 +05:30
NanuSai 08fa726dc1 Tests pass 2021-06-09 21:26:23 +05:30
NanuSai 0f597ddd11 initialize fitness 2021-06-09 19:23:35 +05:30
NanuSai dd4b475b8c All tests compile 2021-06-09 12:49:28 +05:30
NanuSai 7d7ad342ee => cache converted upper and lowerBound
=> PreciisonType flexible
=> Created containers to pass to evaluateobjectives
2021-06-09 12:49:12 +05:30
NanuSai 62dc59099c use MatType lowerBound 2021-06-09 12:47:53 +05:30
NanuSai 556ded8c48 Everything compiles. 2021-06-09 11:14:45 +05:30
NanuSai 34366e7237 perturbationIndex pt.2 2021-06-09 11:07:12 +05:30
NanuSai 274f9f06e9 => Use cleaner Mutate code.
=> Remove redundant for loops.
2021-06-09 11:04:41 +05:30
NanuSai 5fb082a95a distributionIndex => perturbationIndex 2021-06-09 11:03:24 +05:30
NanuSai 2822fae878 Additional docs in declare file 2021-06-09 09:17:36 +05:30
NanuSai 5c11b532bc => Indent fix.
=> Use ElemType in DecomposeObjectives.
=> paretoSet and paretoFront filled.
=> Additional docs.
2021-06-09 09:17:07 +05:30
NanuSai efa5a2efbd Additional docs
remove callback tests
2021-06-08 21:29:16 +05:30
NanuSai b4f862d73f ] => ) 2021-06-08 20:48:28 +05:30
NanuSai 80a2f851b6 g => generation: main loop 2021-06-08 20:32:17 +05:30
NanuSai 57c8d70a02 ideal point uses ElemType 2021-06-08 20:30:58 +05:30
NanuSai a44da082b2 rm StepTaken 2021-06-08 20:23:52 +05:30
NanuSai 6703d07001 Tab fix
Remove const ref for primitive
2021-06-08 20:22:22 +05:30
NanuSai 410eb50fd2 Maintain EvaluateObjectives of NSGA-II 2021-06-08 20:19:32 +05:30
NanuSai e086bf2bd0 Doc fix 2021-06-08 19:58:29 +05:30
NanuSai 391e164fb7 moead_impl.hpp
=> Use BaseMatType
=> Remove redundant checks
2021-06-08 19:47:58 +05:30
NanuSai 1149301fa5 use ParetoSet & ParetoFront in declaration file 2021-06-08 10:29:43 +05:30
NanuSai 2ce3bd3d14 tab v space pt3 2021-06-08 10:21:16 +05:30
NanuSai e515851b40 author fix impl
test comments fixed.
2021-06-08 10:21:16 +05:30
NanuSai 47c981ec3a fix indendations pt2 2021-06-08 10:21:16 +05:30
NanuSai bfde39de2f tab v space 2021-06-08 10:21:16 +05:30
Nanubala Gnana SaiandMarcus Edel 9d7eec84b9 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-08 10:21:16 +05:30
Nanubala Gnana SaiandMarcus Edel c849873522 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-08 10:21:16 +05:30
Nanubala Gnana SaiandMarcus Edel faa94ab15b Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-08 10:21:16 +05:30
Nanubala Gnana SaiandMarcus Edel 83e0850cc7 Update include/ensmallen_bits/moead/moead_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-08 10:21:16 +05:30
NanuSai f516864104 arma::square 2021-06-08 10:21:16 +05:30
NanuSai c602dc1386 bound fix in optimizer.MD 2021-06-08 10:21:16 +05:30
NanuSai 9c888154b0 fix bounds 2021-06-08 10:21:16 +05:30
NanuSai 8a52d9ab10 add callbacktest 2021-06-08 10:21:16 +05:30
NanuSai a95c0e647a Remove shuffle from problems 2021-06-08 10:21:16 +05:30
NanuSai f38f6a1d44 modify test a bit 2021-06-08 10:21:16 +05:30
NanuSai 73ef983352 maxreplace is always positive 2021-06-08 10:21:16 +05:30
NanuSai d2615d5471 optimizer.MD update 2021-06-08 10:21:16 +05:30
NanuSai 5e27bdf1f2 minor doc changes 2021-06-08 10:21:16 +05:30
NanuSai f1488ab44e candidateFval => candidateFitness 2021-06-08 10:21:16 +05:30
NanuSai 3f30929c31 => test with def args : pass
=> rm arma::Mat<> => arma::umat
2021-06-08 10:21:16 +05:30
NanuSai be8e45b45a rm
a) mutationProb
 b) mutationStrength

add
 Sanity checks for all params

Styling fix
2021-06-08 10:21:16 +05:30
NanuSai c388bde9ea rm redundant args 2021-06-08 10:21:16 +05:30
NanuSai 33bcfca29d optimizer.md updated 2021-06-08 10:21:16 +05:30
NanuSai 288b3d710a change def args:
- numGen should be 2k
- paper says neighborProb should be less (for diversity) => 0.3
2021-06-08 10:21:16 +05:30
NanuSai b1bd1445ce numGenerations => maxGenerations 2021-06-08 10:21:16 +05:30
Nanubala Gnana SaiandMarcus Edel e73e79de9b Update include/ensmallen_bits/moead/moead.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-08 10:21:16 +05:30
Nanubala Gnana SaiandMarcus Edel eaf86e9c22 Update doc/optimizers.md
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-08 10:21:16 +05:30
Nanubala Gnana SaiandMarcus Edel cab48a009d Update doc/optimizers.md
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-08 10:21:16 +05:30
NanuSai c380dd5d7d rname MOEAD => MOEA/D-DE 2021-06-08 10:21:16 +05:30
NanuSai e04519b936 fix authors 2021-06-08 10:21:16 +05:30
NanuSai cf9ec55754 some work on optimizer.md 2021-06-08 10:21:16 +05:30
NanuSai ee92ed0f7b HISTORY.md updated 2021-06-08 10:21:16 +05:30
NanuSai c067fcc68e fonseca shuffle fix 2021-06-08 10:21:16 +05:30
NanuSai 2e8ba4d144 undo nsga2_test changes 2021-06-08 10:21:16 +05:30
NanuSai aecbe43c3b - Removed preserveDiversity for now
- ALL TESTS PASSED!!!
2021-06-08 10:20:45 +05:30
NanuSai 8b2ae1312d rm preserve diversity 2021-06-08 10:20:45 +05:30
NanuSai 57815a59b0 Shuffle func is wrong 2021-06-08 10:20:45 +05:30
NanuSai fefcaa6b1f minor style change test 2021-06-08 10:20:45 +05:30
NanuSai 08b24f1bc8 - Everything compiles
- All tests fail
-  debug shows weight 0?

Finally hit the "wall".
2021-06-08 10:20:45 +05:30
NanuSai b1ba5a2123 - n_rows
- evaluate should take const population
2021-06-08 10:20:45 +05:30
NanuSai 8b8fe49f7c use arma::datum::eps
add check for lowerBound and upperBound
2021-06-08 10:20:45 +05:30
NanuSai 41d58bc652 minor 2021-06-08 10:20:45 +05:30
NanuSai 1011370953 mutation fixed 2021-06-08 10:20:45 +05:30
NanuSai ed5ad0e53b DE done 2021-06-08 10:20:45 +05:30
NanuSai 7d50c879fc Nits on MatingSelection
-  assert neighborSize > 1
-  minors
2021-06-08 10:20:45 +05:30
NanuSai d794746062 styling fix 2021-06-08 10:20:45 +05:30
NanuSai c281336e64 fix chronology in docs 2021-06-08 10:20:45 +05:30
NanuSai d67ed85dd0 remove
- dominates
rename
- differentialWeight, changed default value
- add
weight stability
2021-06-08 10:20:45 +05:30
NanuSai 73a3b90c1c tchebycheff is max 2021-06-08 10:20:45 +05:30
NanuSai 99f6f2b630 rm:
- externalPopulation
 - externalPopulationFval
 - update neighbor of MOEA/D
 rename:
- FValue => populationFval
- Add
 - update solutions as MOEA/D-DE prefers
 -  use diversity preserve conditions
 -  idealPoint and candidateFval are arma::vec

========
REMAINING
- Fixing the final stuff
- Some additional touches
- Possible code fixes
2021-06-08 10:20:45 +05:30
NanuSai a4dcccd26a rename
-  DecomposeObjectives
2021-06-08 10:20:45 +05:30
NanuSai d69c899bfd Add params
- maxReplace
- preserveDiversity
- Add docs
2021-06-08 10:20:45 +05:30
NanuSai 89f6b8fb41 - fix compile errors
- test still fail
2021-06-08 10:20:45 +05:30
NanuSai 0ae80352d8 - add TODO
- add FIXME
- neighborIndices
2021-06-08 10:20:45 +05:30
NanuSai 09ec4a23ac Scalingfactor in impl
pFlag switch Mating
2021-06-08 10:20:45 +05:30
NanuSai ad8383ebad Add enum for P type 2021-06-08 10:20:45 +05:30
NanuSai a3b812a283 weightNeighborIndex fix 2021-06-08 10:20:45 +05:30
NanuSai f4c348a316 MatingSelection needs popIdx
weighNeighbourMatrix idx fix
2021-06-08 10:20:45 +05:30
NanuSai 5a07b38a3a evaluatedCandidate => candidateFval 2021-06-08 10:20:45 +05:30
NanuSai b01e7fab18 * scalingFactor in ctor
* paper says 0.4  -> 1 is a good for scaling factor
2021-06-08 10:20:45 +05:30
NanuSai ded4a43837 1.f / numVariables, should pass float 2021-06-08 10:20:45 +05:30
NanuSai 28be655f05 numVariables 2021-06-08 10:20:45 +05:30
NanuSai 3cd94aeb67 scalingFactor getter setter 2021-06-08 10:20:45 +05:30
NanuSai bb414e68ed convention 2021-06-08 10:20:45 +05:30
NanuSai a52f1ad5f4 cast to vector only at EvaluateObjectives 2021-06-08 10:20:45 +05:30
NanuSai 3d91ccc37f differential crossover 2021-06-08 10:20:45 +05:30
NanuSai 91df7068c5 crossover inside mainloop 2021-06-08 10:20:45 +05:30
NanuSai 19425d4d78 - wrap mating in a function
- declare DifferentialCrossover
2021-06-08 10:20:45 +05:30
NanuSai 0419469103 - Import previous work
- populationSize > neighborSize + 1
- fix initNeighborhood
- neighborHoodSize => neighborSize
2021-06-08 10:20:45 +05:30
Nanubala Gnana Sai c22c6fc420 Add docs for ZDT_ONE Test 2021-06-08 06:59:34 +05:30
NanuSai 6e9287df1a Fix re-ordering warning 2021-06-06 11:38:14 +05:30
Marcus Edel 447f8086ff Merge branch 'master' into ZDT 2021-06-05 22:25:35 -04:00
Marcus Edel abd5b036f1 Merge pull request #289 from jonpsy/moo-callback
Make Callback flexible for MultiObjective Optimizers.
2021-06-04 09:58:24 -04:00
Marcus Edel 2306acb37e Merge pull request #291 from jonpsy/frontdoc
Doc fix.
2021-06-02 09:32:23 -04:00
NanuSai 6d28ed41c6 rm query front doc in ctor 2021-06-01 22:32:12 +05:30
NanuSai 3eb05e9ddf Merge branch 'moo-callback' of https://github.com/jonpsy/ensmallen into moo-callback 2021-06-01 22:29:51 +05:30
NanuSai 72ced21bb4 history fix 2021-06-01 22:29:33 +05:30
Nanubala Gnana SaiandMarcus Edel 6d4023c855 Update include/ensmallen_bits/callbacks/query_front.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-06-01 22:14:36 +05:30
NanuSai 29b6ff1974 fix 2021-06-01 15:12:19 +05:30
NanuSai 526f1d449f init gensteptaken 2021-06-01 15:03:34 +05:30
NanuSai e6c4513df4 remov e ZDT5 2021-06-01 14:56:23 +05:30
NanuSai e7215436fe fix test for MOO 2021-06-01 14:43:56 +05:30
NanuSai b5e85c9874 Add inbuilt query front hpp 2021-06-01 14:43:48 +05:30
NanuSai 873d951e2e add docs in callbacks.md 2021-06-01 14:43:30 +05:30
NanuSai 5d05ff62ec => Rename to GenerationalStepTaken
=> Move Callback::StepTaken() at the end of for loop nsga2
=> Register GenerationStepTaken in macro
2021-06-01 10:10:03 +05:30
NanuSai 5082d82133 history 2021-05-29 12:21:51 +05:30
NanuSai 5afdcdd9e3 replace callback in nsga2 2021-05-29 11:50:31 +05:30
NanuSai 4a45156130 remove redundant docs 2021-05-29 11:47:28 +05:30
NanuSai 25c4af46bf indendation fix 2021-05-29 11:38:40 +05:30
NanuSai 5853712ba1 1. fix compile error
2. rename to ObjectivesVecType
2021-05-29 11:32:41 +05:30
Nanubala Gnana Sai eb345f1346 Initial push.
=> add MOOTypedForms in traits.hpp.
=> added a specialization for StepTaken for MOO in callbacks.hpp.
2021-05-29 03:05:36 +05:30
Nanubala Gnana Sai a6e8291c66 Document that reference front plot is in the PR. 2021-05-26 22:50:31 +05:30
Nanubala Gnana Sai 4136639d4e Add ZDT1 Test 2021-05-26 22:50:04 +05:30
Nanubala Gnana Sai 2c22ab2407 GetParetoFront => GetReferenceFront 2021-05-25 22:06:04 +05:30
NanuSai d40b12fa77 add ZDT to nsga2 (not working) 2021-05-25 19:08:30 +05:30
NanuSai 3200754617 numParetoPoints specified by user 2021-05-25 19:08:18 +05:30
Marcus Edel f66643e1c5 Merge pull request #288 from zoq/catch-main-seed
Use Catch2 seed command-line argument to set ensmallen seeds.
2021-05-17 19:37:36 -04:00
Marcus Edel b24589bacb Add AdaBelief to the function types documentation. 2021-05-16 23:44:49 +02:00
NanuSai 118bc4ebaa fix compile errors 2021-05-14 10:57:10 +05:30
NanuSai e4c3a63375 Pareto Front for ZDT-4,5,6 2021-05-14 10:43:12 +05:30
NanuSai d34aec8cf8 fix indent 2021-05-14 10:08:11 +05:30
NanuSai 4baf1072e0 fix history 2021-05-14 10:06:49 +05:30
NanuSai 1276ef366b * Pareto Front for ZDT-1,2,3
* Put inside folder
2021-05-14 10:04:53 +05:30
NanuSai f36a517358 Add Pareto Front for ZDT1, ZDT2 2021-05-14 10:04:53 +05:30
NanuSai 6a9a44f62a move to folder 2021-05-14 10:04:53 +05:30
NanuSai 1f7bb9a730 ZDT5 added 2021-05-14 10:04:53 +05:30
NanuSai e6b130bb24 ZDT history 2021-05-14 10:04:53 +05:30
Nanubala Gnana Sai e7b26115f4 Merge branch 'master' into ZDT 2021-05-04 13:05:23 +05:30
Ryan Curtin 27150fc8fd Merge pull request #287 from zoq/ci-dist-update
Switch from Ubuntu Trusty 14.04 to Ubuntu Focal 20.04
2021-05-02 22:34:08 -04:00
Marcus Edel 2751e7968f Merge pull request #285 from jonpsy/indicator
Add MOO Indicators.
2021-05-02 12:04:53 -04:00
Marcus Edel 6e3fbc278e Use Catch2 seed command-line argument to set ensmallen seeds. 2021-05-02 04:15:53 +02:00
Marcus Edel 55f2e6aac2 Use updated test suite functionality. 2021-05-02 04:11:43 +02:00
Marcus EdelandRyan Curtin 6458263f05 Spelling fix and remove duplicated description.
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2021-05-01 22:11:27 -04:00
Marcus Edel c954ab231c Merge branch 'master' into ada_belief 2021-05-01 15:07:10 -04:00
Nanubala Gnana Sai 0a1504ef2a Add braces 2021-04-29 01:45:37 +05:30
Nanubala Gnana SaiandMarcus Edel e975ee968f Update include/ensmallen_bits/utility/indicators/epsilon.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-04-29 01:34:17 +05:30
Nanubala Gnana SaiandMarcus Edel 00020ce08a Update include/ensmallen_bits/nsga2/nsga2.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-04-29 01:33:59 +05:30
Marcus Edel 61def4a04b Switch from Ubuntu Trusty 14.04 to Ubuntu Focal 20.04. 2021-04-27 02:41:51 +02:00
NanuSai 284b2f9d30 separate API for paretoset and paretofront 2021-04-26 00:14:59 +05:30
NanuSai 70ed598f53 redundant include 2021-04-25 23:53:00 +05:30
NanuSai 98ba659e86 restore cmakelists.txt 2021-04-25 23:52:03 +05:30
Nanubala Gnana Sai c013fcbaa6 Update .appveyor.yml 2021-04-25 23:48:23 +05:30
Nanubala Gnana Sai 6f79de7668 Update .appveyor.yml 2021-04-25 23:47:50 +05:30
NanuSai 9099b7400c revert appveyor. 2021-04-25 23:46:50 +05:30
NanuSai a62eda2ecc fix float error
compile only indicators
2021-04-25 23:30:00 +05:30
NanuSai 69361e98e5 Hence begins the RDP quest! 2021-04-25 22:48:40 +05:30
Nanubala Gnana Sai 18f7f2a736 use templated std::max
revert tolerance
2021-04-25 11:51:56 +05:30
Nanubala Gnana Sai a977492626 Update indicators_test.cpp
Change tolerance for MSVC15 (Assuming tolerance is causing the problem which I think it isn't, we'll see)
2021-04-25 02:16:29 +05:30
NanuSai b56b5b99c5 => epsilon ctor fix
=> add indicators test
=> Results match with hand calculated values.
2021-04-24 22:01:23 +05:30
NanuSai a1bd37c9a3 => rename igd -> igd+.
=> add public ctor similar to Metric Policy.
=> std::max() fix
2021-04-24 22:00:17 +05:30
NanuSai a26e271960 Zero div handled 2021-04-24 14:58:49 +05:30
Nanubala Gnana Sai ec75ddc04b -> further doc fixes in ensmallen.hpp
-> fix minor error in igd+
2021-04-24 01:04:09 +05:30
Nanubala Gnana Sai 7302ce1093 Doc cleanups 2021-04-24 00:32:20 +05:30
NanuSai 8613e1ba11 => Register at ensmallen.hpp
=> static and ElemType in Evaluate()
2021-04-17 13:35:29 +05:30
Nanubala Gnana Sai 5d90357602 Update nsga2_impl.hpp
fix spacing issue in github
2021-04-17 13:16:07 +05:30
NanuSai cc9d349f1f impl file fixed
test fixed
all test pass
2021-04-17 13:12:14 +05:30
NanuSai 8167ed1185 header arma::cube 2021-04-17 07:24:54 +05:30
NanuSai 41832e401c move to utility 2021-04-17 07:23:52 +05:30
NanuSai 5e2b5747cc shift doc to left 2021-04-16 09:08:47 +05:30
NanuSai 7acf499ceb nits 2021-04-16 09:03:58 +05:30
NanuSai 80955a75e5 spacing issues 2021-04-14 12:13:51 +05:30
Nanubala Gnana Sai 176c24b4fb Update History.md 2021-04-14 12:13:24 +05:30
Nanubala Gnana Sai ec9a93b171 Steal @favre49's work 2021-04-14 12:12:43 +05:30
NanuSai c2dfef1556 Minor style issues 2021-04-14 11:54:14 +05:30
Nanubala Gnana SaiandMarcus Edel 24367895df Update include/ensmallen_bits/problems/zdt1_function.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-04-14 11:54:14 +05:30
Nanubala Gnana SaiandMarcus Edel 499458d20f Update include/ensmallen_bits/problems/zdt1_function.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-04-14 11:54:14 +05:30
NanuSai 69520fbaa4 minor indent fix T_T 2021-04-14 11:54:14 +05:30
NanuSai 6894b7e03c ZDT history 2021-04-14 11:54:14 +05:30
NanuSai 8cbac229ad ElemType 2021-04-14 11:54:14 +05:30
NanuSai 7d20f81cfb => MatType return
=> Indent fix
2021-04-14 11:54:14 +05:30
NanuSai a62416b974 inner access outer. 2021-04-14 11:54:14 +05:30
NanuSai f9f3dc28ea Test working against pagmo2 2021-04-14 11:54:14 +05:30
NanuSai 7ac386e1ec Added
1) ZDT1
2) ZDT2
3) ZDT3
4) ZDT4
5) ZDT6
2021-04-14 11:54:14 +05:30
NanuSai 6924cf1c32 initial push
=> Documentation done.
=> Declared the class.
2021-04-14 11:54:14 +05:30
Marcus Edel 1e70823989 Merge pull request #263 from jonpsy/nsga2-better
Improvise NSGA2.
2021-04-13 20:04:39 -04:00
NanuSai 33974ce3f7 check arbitrary 2021-04-11 00:04:51 +05:30
Nanubala Gnana Sai 9463b153d1 Update HISTORY.md
white space
2021-04-10 23:57:11 +05:30
Nanubala Gnana Sai a4f320a418 Merge branch 'master' into nsga2-better 2021-04-10 23:56:17 +05:30
Marcus Edel 1b6c1d93f0 Merge pull request #283 from jonpsy/moo_traits
Recursively check for Evaluate() in MOO.
2021-04-10 13:34:22 -04:00
Marcus Edel 388bcd82cf Merge pull request #284 from zoq/code-analyzer-fixes
Code analyzer adjustments.
2021-04-09 17:04:56 -04:00
Nanubala Gnana Sai 6be8403fbb Merge branch 'nsga2-better' of https://github.com/jonpsy/ensmallen into nsga2-better 2021-04-09 01:29:42 +05:30
NanuSai 6714a178af Merge branch 'nsga2-better' of https://github.com/jonpsy/ensmallen into nsga2-better 2021-04-09 00:19:57 +05:30
Nanubala Gnana Sai 850612fd97 sanity fix 2021-04-08 19:14:01 +05:30
Marcus Edel 1cd90c6158 Initialize all class parameters. 2021-04-07 22:55:15 +02:00
Marcus Edel bccdbcc7de Remove unused parameter 'numBatches'. 2021-04-07 22:52:25 +02:00
Marcus Edel 089e9395de No need to worry about division by zero. 2021-04-07 22:51:33 +02:00
NanuSai ad8538602d fix compile time error 2021-04-08 01:17:36 +05:30
Nanubala Gnana Sai c6e76e2c62 indent fix 2021-04-07 09:38:47 +05:30
Nanubala Gnana Sai 782b994765 rm unsigned symnb 2021-04-07 09:26:11 +05:30
Nanubala Gnana SaiandMarcus Edel 8ecd483bd4 Update include/ensmallen_bits/function/static_checks.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2021-04-07 09:01:41 +05:30
Nanubala Gnana Sai 7bffa5df48 restore inline 2021-04-04 19:38:04 +05:30
Nanubala Gnana Sai 6f4362b23e style fixing 2021-04-04 19:35:40 +05:30
Nanubala Gnana Sai 750043d20a - reuse established API
- doc change for MOO trait
2021-04-04 19:31:42 +05:30
Nanubala Gnana Sai 563457848e Added History.MD 2021-04-04 12:57:31 +05:30
NanuSai ccf0dff819 ryans method 2021-04-04 15:50:06 +05:30
NanuSai 0069291572 temporary solution in:
a) static_check (possibly wrong)
b) check Arbitrary in for loop in EvaluateObjectives
2021-04-04 14:18:21 +05:30
NanuSai 9fc7013d8d type trait for multiarbitraryfunction 2021-04-03 23:51:42 +05:30
Nanubala Gnana Sai 02d8d94c18 appveyor from master 2021-04-02 07:18:18 +05:30
Nanubala Gnana Sai 7eafb8a2f5 Revert changes from RDP 2021-04-02 07:06:49 +05:30
Nanubala Gnana Sai 5c31f6ad51 stress test on appveyor proofs => run 3k times 2021-04-01 09:37:22 +05:30
Nanubala Gnana Sai 95bc44f4fa rm appveyor pass 2021-04-01 01:09:51 +05:30
Nanubala Gnana Sai fb5f23db8b revert appveyor and ctest settings 2021-04-01 01:08:30 +05:30
Nanubala Gnana Sai f3f699a508 run appveyor test 3000 times 2021-04-01 00:52:30 +05:30
Nanubala Gnana Sai 400720aa15 appveyor print when test fails 2021-04-01 00:47:00 +05:30
Nanubala Gnana Sai 36e43ae21f run only nsga2 test and use random seeds in main.cpp 2021-03-31 15:16:37 +05:30
Nanubala Gnana Sai 846b01f165 appveyor debug 2021-03-31 15:08:50 +05:30
NanuSai f58e0eba43 fix windows indentation 2021-03-31 12:24:31 +05:30
Nanubala Gnana Sai 252c500696 indent fix 2021-03-31 12:15:47 +05:30
Nanubala Gnana Sai d9f3e7b7f2 Update nsga2_impl.hpp
indent fix
2021-03-31 12:11:43 +05:30
Nanubala Gnana Sai b986687494 fix merge conflicts 2021-03-31 12:06:29 +05:30
Nanubala Gnana Sai bad9062cd6 increase tolerance 2021-03-31 11:57:46 +05:30
NanuSai 68157eede0 build in debug 2021-03-30 19:29:08 +05:30
NanuSai 12c513d26c appveyor debug to check tolerance level 2021-03-30 14:32:06 +05:30
Nanubala Gnana Sai 15618563b9 introduced tolerance 2021-03-30 11:17:18 +05:30
Nanubala Gnana Sai a975138f4e appveyor old 2021-03-30 02:37:26 +05:30
Nanubala Gnana Sai c98b16acd1 SchafferN1 double debug Appveyor 2021-03-30 01:40:03 +05:30
Nanubala Gnana Sai d3b2ce932a Use params from paper
1) crossoverProb: 0.9
2) mutationRate: 1/n; n = num vars
3) numGeneration = 250
2021-03-30 01:31:14 +05:30
Nanubala Gnana Sai 70bfceebc5 Reason for changing:
a) 5k gen is too much, papers stick to ~150
b) The test error which I get is off by 1e-2
c)  Solutions are loosing diversity at higher number of generations (repeating solutions even)
2021-03-29 22:30:04 +05:30
NanuSai 1ad7181e84 everyone use IsInBounds 2021-03-28 15:26:00 +05:30
NanuSai 38f88ab2f2 minor 2021-03-28 10:01:54 +05:30
NanuSai d9c049606a maybe the error should be ok now 2021-03-28 10:00:27 +05:30
NanuSai 2689d5c19e use absolute diff 2021-03-28 09:14:24 +05:30
NanuSai 1d16313112 build in debug 2021-03-28 08:54:09 +05:30
NanuSai d18192e251 building works, now just test fails on double 2021-03-28 07:27:04 +05:30
NanuSai 679d904539 cxx11 compiler error? 2021-03-28 05:55:13 +05:30
NanuSai 6c388a0cac appveyor checks again 2021-03-28 05:38:46 +05:30
NanuSai 0256405a4f check error 2021-03-28 05:33:31 +05:30
Ryan Birmingham 9f90ec92aa Merge pull request #281 from zoq/cmaes-doc-fix
Fix CMAES population size parameter comment
2021-03-27 12:07:55 -04:00
Marcus Edel 3d108b2313 Fix CMAES population size parameter comment. 2021-03-27 02:18:36 +01:00
Ryan Curtin 6dc293ec1d Add new block for next release to HISTORY.md. 2021-03-25 16:47:25 +01:00
NanuSai 1b0b51f570 appveyor to normal
MSVC Works!
2021-03-23 16:58:29 +05:30
NanuSai a1ef11a077 some more removals 2021-03-23 16:15:44 +05:30
NanuSai c19c5f6870 MSVC-15 fails back_inserter 2021-03-23 14:54:20 +05:30
NanuSai f76c2f2ae7 on finish 2021-03-23 14:19:56 +05:30
NanuSai a691584fa3 dont run test as well 2021-03-23 14:15:16 +05:30
NanuSai a43c447c84 dont build! 2021-03-23 14:08:59 +05:30
NanuSai 6254ac14b2 remove some inlines 2021-03-23 13:58:22 +05:30
NanuSai 8f6af30aec appveyor validated 2021-03-23 11:54:14 +05:30
NanuSai 1c290c3235 back2square1 2021-03-23 11:48:58 +05:30
Nanubala Gnana Sai eec6cad594 Update .appveyor.yml 2021-03-23 11:13:01 +05:30
Nanubala Gnana Sai 69906e100c test appveyor 2021-03-23 11:01:56 +05:30
NanuSai 31e96e2bb4 make class template 2021-03-22 20:21:54 +05:30
NanuSai e508278039 single threaded 2021-03-22 18:01:32 +05:30
NanuSai b12d050fd5 a minor change to spot error(will be removed) 2021-03-22 17:47:32 +05:30
NanuSai b2f525f5aa allow floatonly 2021-03-22 17:47:18 +05:30
Nanubala Gnana Sai 01dbfa3e7b Merge branch 'master' into nsga2-better 2021-03-22 16:11:43 +05:30
NanuSai e8506960a0 Changes (See below)
=> Fix crowdingAssignment
    => Type robust
    => Add bounds to initial population
    => Reduced if / for blocks
    => Added float test
    => Minor additional docs
2021-03-20 09:45:28 +05:30
Marcus Edel 6066feed05 Merge branch 'master' into DemonSGD 2020-12-01 22:07:00 +01:00
Marcus EdelandRyan Curtin 5e4a914f38 Minor spelling fix.
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-12-01 22:05:52 +01:00
Marcus EdelandRyan Curtin 0265d8b31e Minor spelling fix.
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-12-01 22:05:38 +01:00
Marcus EdelandRyan Curtin 7d9ceb1491 Minor spelling fix.
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-12-01 22:05:28 +01:00
Marcus Edel f944ad4334 Add missing AdaSqrt test file. 2020-10-25 21:30:00 +01:00
Marcus Edel 5ffb6631f6 Update history - add AdaSqrt optimizer. 2020-10-25 21:27:22 +01:00
Marcus Edel 8e646faf4e Add AdaSqrt optimizer documentation. 2020-10-25 21:25:50 +01:00
Marcus Edel 204379ba65 Add AdaSqrt optimizer test cases. 2020-10-25 21:25:25 +01:00
Marcus Edel 77a6c3d16a Add AdaSqrt optimizer. 2020-10-25 21:25:05 +01:00
Marcus Edel a8be895fac Update histroy - add AdaBelief optimizer. 2020-10-22 01:20:53 +02:00
Marcus Edel 2f812cd01b Add AdaBelief documentation. 2020-10-22 01:19:01 +02:00
Marcus Edel d4802dbf46 Add AdaBelief test cases. 2020-10-22 01:18:34 +02:00
Marcus Edel 028b6cf9bd Add AdaBelief optimizer. 2020-10-22 01:17:56 +02:00
Marcus Edel 2cfbdad7fb Remove unused parameter. 2020-10-21 18:00:32 +02:00
Marcus Edel 5397799a5f Update histroy - add Yogi optimizer. 2020-10-21 00:32:46 +02:00
Marcus Edel 8bafa47040 Add Yogi test cases. 2020-10-21 00:29:49 +02:00
Marcus Edel 1d5e900381 Add Yogi documentation. 2020-10-21 00:28:18 +02:00
Marcus Edel fa7f682480 Add Yogi optimizer. 2020-10-19 23:48:06 +02:00
Marcus Edel c8f205c919 Merge branch 'master' into DemonSGD 2020-08-29 23:50:53 +02:00
Marcus Edel a64420b1f0 Make sure the momentum iterations parameter is non-zero. 2020-08-08 21:26:16 +02:00
Marcus Edel cd4a6366f8 Use number of iterations and batch size to intalize the momentum iterations. 2020-08-08 21:08:34 +02:00
Marcus Edel 4dd4cc4804 Remove momentum iterations parameter. 2020-08-08 21:07:26 +02:00
Marcus Edel 7af640705e Explicit type cast to avoid the loss of a fractional part and minor style fix. 2020-08-02 20:13:38 +02:00
Marcus Edel a57ff2d510 Explicit type cast to avoid the loss of a fractional part. 2020-08-02 17:49:25 +02:00
Marcus Edel 584cfe1de1 Minor style fix (indentation). 2020-08-02 17:47:42 +02:00
Marcus Edel 00d5304284 Add DemonSGD and DemonAdam optimizer to the history. 2020-08-02 17:40:30 +02:00
Marcus Edel 42bd092f87 Add DemonSGD and DemonAdam optimizer documentation. 2020-08-02 17:38:02 +02:00
Marcus Edel 006137ea63 Add DemonAdam optimizer tests. 2020-08-02 17:36:59 +02:00
Marcus Edel 1124e63fda Add DemonAdam optimizer. 2020-08-02 17:36:49 +02:00
Marcus Edel 760f9b9968 Add DemonSGD optimizer tests. 2020-08-02 17:36:27 +02:00
Marcus Edel dfc5dd3198 Add DemonSGD optimizer. 2020-08-02 17:36:05 +02:00
323 changed files with 24981 additions and 5367 deletions
+10 -8
View File
@@ -1,13 +1,9 @@
environment:
ARMADILLO_DOWNLOAD: "http://ftp.fau.de/macports/distfiles/armadillo/armadillo-8.400.0.tar.xz"
ARMADILLO_DOWNLOAD: "https://sourceforge.net/projects/arma/files/armadillo-10.8.2.tar.xz"
BLAS_LIBRARY: "%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a"
BLAS_LIBRARY_DLL: "%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll"
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
VSVER: Visual Studio 14 2015 Win64
MSBUILD: C:\Program Files (x86)\MSBuild\14.0\bin\MSBuild.exe
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VSVER: Visual Studio 15 2017 Win64
MSBUILD: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe
@@ -16,6 +12,10 @@ environment:
VSVER: Visual Studio 16 2019
MSBUILD: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
VSVER: Visual Studio 17 2022
MSBUILD: C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe
configuration: Release
install:
@@ -26,13 +26,14 @@ build_script:
- cd ..
- appveyor DownloadFile %ARMADILLO_DOWNLOAD% -FileName armadillo.tar.xz
- 7z x armadillo.tar.xz -so -txz | 7z x -si -ttar > nul
- cd armadillo-8.400.0 && mkdir build && cd build
- cd armadillo-10.8.2 && mkdir build && cd build
- >
cmake -G "%VSVER%"
-DBLAS_LIBRARY:FILEPATH=%BLAS_LIBRARY%
-DLAPACK_LIBRARY:FILEPATH=%BLAS_LIBRARY%
-DCMAKE_PREFIX:FILEPATH="%APPVEYOR_BUILD_FOLDER%/armadillo"
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
-DCMAKE_BUILD_TYPE=Release ..
- >
"%MSBUILD%" "armadillo.sln"
@@ -43,14 +44,15 @@ build_script:
- cd ensmallen && mkdir build && cd build
- >
cmake -G "%VSVER%"
-DARMADILLO_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER%/../armadillo-8.400.0/include/
-DARMADILLO_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER%/../armadillo-10.8.2/include/
-DARMADILLO_LIBRARIES=%BLAS_LIBRARY%
-DLAPACK_LIBRARY=%BLAS_LIBRARY%
-DBLAS_LIBRARY=%BLAS_LIBRARY%
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
-DCMAKE_BUILD_TYPE=Release ..
- >
"%MSBUILD%" "ensmallen.sln"
/m /verbosity:minimal /nologo /p:BuildInParallel=true
/m /verbosity:minimal /nologo /p:BuildInParallel=false
# Run tests after copying libraries.
- ps: cp C:\projects\ensmallen\OpenBLAS.0.2.14.1\lib\native\bin\x64\*.* C:\projects\ensmallen\build\
+22
View File
@@ -0,0 +1,22 @@
# Once a PR has been approved by one member of the mlpack organization, a second
# approving review will automatically be added 24 hours later. This allows time
# for other maintainers to take a look.
name: Auto-approve pull requests
on:
schedule:
# Run roughly every four hours.
- cron: "15 0,4,8,12,16,20 * * *"
jobs:
auto-approve:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Auto-approve pull requests
uses: rcurtin/actions/auto-approve@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
approval-message:
'Second approval provided automatically after 24 hours. :+1:'
+54
View File
@@ -0,0 +1,54 @@
name: Build and Test
on: [push, pull_request]
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
outputs:
tag: ${{ steps.git.outputs.tag }}
strategy:
fail-fast: false
matrix:
config:
- { name: 'CUDA', os: self-hosted}
- { name: 'OpenCL', os: self-hosted}
- { name: 'CPU', os: self-hosted}
steps:
- uses: actions/checkout@v4
- name: Install Dependencies
run: |
cd ../
rm -rf bandicoot-code
git clone --depth 1 --branch 2.1.1 https://gitlab.com/bandicoot-lib/bandicoot-code.git
cd bandicoot-code/
mkdir build/
cd build/
if [[ "${{ matrix.config.name }}" == "CUDA" ]]; then
echo "Installing Bandicoot CUDA"
cmake -DFIND_CUDA=ON -DFIND_OPENCL=OFF -DBUILD_TESTS=OFF ../
make
elif [[ "${{ matrix.config.name }}" == "OpenCL" ]]; then
echo "Installing Bandicoot OpenCL"
cmake -DFIND_CUDA=OFF -DFIND_OPENCL=ON -DBUILD_TESTS=OFF ../
make
fi
- name: Build ensmallen
run: |
mkdir build
cd build/
if [[ "${{ matrix.config.name }}" == "CPU" ]]; then
cmake -DUSE_BANDICOOT=OFF ..
else
cmake -DBANDICOOT_INCLUDE_DIR=../../bandicoot-code/build/tmp/include/ -DBANDICOOT_LIBRARY=../../bandicoot-code/build/libbandicoot.so ..
fi
make ensmallen_tests
- name: Test ensmallen
run: |
cd build/
./ensmallen_tests -d yes
+24
View File
@@ -0,0 +1,24 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
days-before-issues-stale: 30
days-before-issue-close: 7
stale-issue-label: "s: stale"
stale-pr-label: "s: stale"
stale-issue-message: "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! :+1:"
days-before-pr-stale: 30,
days-before-pr-close: 14
repo-token: ${{ secrets.GITHUB_TOKEN }}
exempt-issue-labels: "s: keep open"
exempt-pr-labels: "s: keep open"
+17
View File
@@ -0,0 +1,17 @@
# Post a message to new contributors that they can get some stickers mailed to
# them.
name: 'Stickers for new contributors'
on:
pull_request:
types: [closed]
jobs:
sticker_comment:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
# Forked version of first-interaction that runs only on first merged PR.
- uses: rcurtin/actions/stickers@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: "Hello there! Thanks for your contribution. Congratulations on your first contribution to mlpack! If you'd like to add your name to the list of contributors in `COPYRIGHT.txt` and you haven't already, please feel free to push a change to this PR---or, if it gets merged before you can, feel free to open another PR.\n\nIn addition, if you'd like some stickers to put on your laptop, we can get them in the mail for you. Just send an email with your physical mailing address to stickers@mlpack.org, and then one of the mlpack maintainers will put some stickers in an envelope for you. It may take a few weeks to get them, depending on your location. :+1:"
+3 -3
View File
@@ -1,5 +1,5 @@
os: linux
dist: trusty
dist: focal
language: cpp
env:
@@ -23,10 +23,10 @@ script:
- if [ $ARMADILLO == "latest" ]; then
curl https://ftp.fau.de/macports/distfiles/armadillo/`curl https://ftp.fau.de/macports/distfiles/armadillo/ -- | grep '.tar.xz' | sed 's/^.*<a href="\(armadillo-[0-9]*.[0-9]*.[0-9]*.tar.xz\)".*$/\1/' | tail -1` | tar xvJ && cd armadillo*;
else
curl https://ftp.fau.de/macports/distfiles/armadillo/armadillo-8.400.0.tar.xz | tar -xvJ && cd armadillo*;
curl -L https://sourceforge.net/projects/arma/files/armadillo-10.8.2.tar.xz | tar -xvJ && cd armadillo*;
fi
- cmake . && make && sudo make install && cd ..
- mkdir build && cd build && cmake -DCMAKE_CXX_FLAGS="-Werror" -DCMAKE_C_FLAGS="-Werror" .. && make -j2
- mkdir build && cd build && cmake .. && make ensmallen_tests -j2
- CTEST_OUTPUT_ON_FAILURE=1 travis_wait 30 ctest -j2
notifications:
+44
View File
@@ -0,0 +1,44 @@
# - Find clBLAS (includes and library)
# This module defines
# CLBLAS_INCLUDE_DIR
# CLBLAS_LIBRARIES
# CLBLAS_FOUND
# also defined, but not for general use are
# CLBLAS_LIBRARY, where to find the library.
find_path(CLBLAS_INCLUDE_DIR clBLAS.h
/usr/include/
/usr/local/include/
)
set(CLBLAS_NAMES ${CLBLAS_NAMES} clBLAS)
find_library(CLBLAS_LIBRARY
NAMES ${CLBLAS_NAMES}
PATHS /usr/lib64/ /usr/local/lib64/ /usr/lib /usr/local/lib
)
if (CLBLAS_LIBRARY AND CLBLAS_INCLUDE_DIR)
set(CLBLAS_LIBRARIES ${CLBLAS_LIBRARY})
set(CLBLAS_FOUND "YES")
else ()
set(CLBLAS_FOUND "NO")
endif ()
if (CLBLAS_FOUND)
if (NOT CLBLAS_FIND_QUIETLY)
message(STATUS "Found a clBLAS library: ${CLBLAS_LIBRARIES}")
endif ()
else ()
if (CLBLAS_FIND_REQUIRED)
message(FATAL_ERROR "Could not find a clBLAS library")
endif ()
endif ()
# Deprecated declarations.
set (NATIVE_CLBLAS_INCLUDE_PATH ${CLBLAS_INCLUDE_DIR} )
get_filename_component (NATIVE_CLBLAS_LIB_PATH ${CLBLAS_LIBRARY} PATH)
mark_as_advanced(
CLBLAS_LIBRARY
CLBLAS_INCLUDE_DIR
)
+44
View File
@@ -0,0 +1,44 @@
# - Find clBlast (includes and library)
# This module defines
# CLBLAST_INCLUDE_DIR
# CLBLAST_LIBRARIES
# CLBLAST_FOUND
# also defined, but not for general use are
# CLBLAST_LIBRARY, where to find the library.
find_path(CLBLAST_INCLUDE_DIR clblast.h
/usr/include/
/usr/local/include/
)
set(CLBLAST_NAMES ${CLBLAST_NAMES} clblast)
find_library(CLBLAST_LIBRARY
NAMES ${CLBLAST_NAMES}
PATHS /usr/lib64/ /usr/local/lib64/ /usr/lib /usr/local/lib
)
if (CLBLAST_LIBRARY AND CLBLAST_INCLUDE_DIR)
set(CLBLAST_LIBRARIES ${CLBLAST_LIBRARY})
set(CLBLAST_FOUND "YES")
else ()
set(CLBLAST_FOUND "NO")
endif ()
if (CLBLAST_FOUND)
if (NOT CLBLAST_FIND_QUIETLY)
message(STATUS "Found a clBlast library: ${CLBLAST_LIBRARIES}")
endif ()
else ()
if (CLBLAST_FIND_REQUIRED)
message(FATAL_ERROR "Could not find a clBlast library")
endif ()
endif ()
# Deprecated declarations.
set (NATIVE_CLBLAST_INCLUDE_PATH ${CLBLAST_INCLUDE_DIR} )
get_filename_component (NATIVE_CLBLAST_LIB_PATH ${CLBLAST_LIBRARY} PATH)
mark_as_advanced(
CLBLAST_LIBRARY
CLBLAST_INCLUDE_DIR
)
+35
View File
@@ -0,0 +1,35 @@
# - Find clBlast (includes and library)
# This module defines
# CLBLAST_INCLUDE_DIR
# CLBLAST_LIBRARIES
# CLBLAST_FOUND
# also defined, but not for general use are
# CLBLAST_LIBRARY, where to find the library.
set(NVRTC_NAMES ${NVRTC_NAMES} nvrtc)
find_library(NVRTC_LIBRARY
NAMES ${NVRTC_NAMES}
PATHS /usr/lib64/ /usr/local/lib64/ /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu/
)
if (NVRTC_LIBRARY)
set(NVRTC_LIBRARIES ${NVRTC_LIBRARY})
set(NVRTC_FOUND "YES")
else ()
set(NVRTC_FOUND "NO")
endif ()
if (NVRTC_FOUND)
if (NOT NVRTC_FIND_QUIETLY)
message(STATUS "Found NVRTC library: ${NVRTC_LIBRARIES}")
endif ()
else ()
if (NVRTC_FIND_REQUIRED)
message(FATAL_ERROR "Could not find NVRTC library")
endif ()
endif ()
# Deprecated declarations.
get_filename_component (NATIVE_NVRTC_LIB_PATH ${NVRTC_LIBRARY} PATH)
mark_as_advanced(NVRTC_LIBRARY)
+319
View File
@@ -0,0 +1,319 @@
# - Find Bandicoot
# Find Bandicoot: GPU accelerator add-on for the Armadillo C++ linear algebra
# library
#
# Using Bandicoot:
# find_package(Bandicoot REQUIRED)
# include_directories(${BANDICOOT_INCLUDE_DIRS})
# add_executable(foo foo.cc)
# target_link_libraries(foo ${BANDICOOT_LIBRARIES})
# This module sets the following variables:
# BANDICOOT_FOUND - set to true if the library is found
# BANDICOOT_INCLUDE_DIRS - list of required include directories
# BANDICOOT_LIBRARIES - list of libraries to be linked
# BANDICOOT_VERSION_MAJOR - major version number
# BANDICOOT_VERSION_MINOR - minor version number
# BANDICOOT_VERSION_PATCH - patch version number
# BANDICOOT_VERSION_STRING - version number as a string (ex: "1.0.4")
# BANDICOOT_VERSION_NOTE - name of the version (ex: "unstable development version")
find_path(BANDICOOT_INCLUDE_DIR
NAMES bandicoot
PATHS "$ENV{ProgramFiles}/Bandicoot/include"
)
if(BANDICOOT_INCLUDE_DIR)
# Extract version information.
file(READ "${BANDICOOT_INCLUDE_DIR}/bandicoot_bits/coot_version.hpp" _bandicoot_HEADER_CONTENTS)
string(REGEX REPLACE ".*#define COOT_VERSION_MAJOR ([0-9]+).*" "\\1" BANDICOOT_VERSION_MAJOR "${_bandicoot_HEADER_CONTENTS}")
string(REGEX REPLACE ".*#define COOT_VERSION_MINOR ([0-9]+).*" "\\1" BANDICOOT_VERSION_MINOR "${_bandicoot_HEADER_CONTENTS}")
string(REGEX REPLACE ".*#define COOT_VERSION_PATCH ([0-9]+).*" "\\1" BANDICOOT_VERSION_PATCH "${_bandicoot_HEADER_CONTENTS}")
string(REGEX REPLACE ".*#define COOT_VERSION_NOTE\ +\"([0-9a-zA-Z\ _-]+)\".*" "\\1" BANDICOOT_VERSION_NOTE "${_bandicoot_HEADER_CONTENTS}")
set(BANDICOOT_VERSION_STRING "${BANDICOOT_VERSION_MAJOR}.${BANDICOOT_VERSION_MINOR}.${BANDICOOT_VERSION_PATCH}")
endif ()
# Determine what support libraries are being used, and whether or not we need to
# link against them. We need to look in config.hpp.
set(SUPPORT_INCLUDE_DIRS "")
set(SUPPORT_LIBRARIES "")
set(COOT_NEED_LIBRARY true) # Assume true.
if(EXISTS "${BANDICOOT_INCLUDE_DIR}/bandicoot_bits/config.hpp")
file(READ "${BANDICOOT_INCLUDE_DIR}/bandicoot_bits/config.hpp" _bandicoot_CONFIG_CONTENTS)
# COOT_USE_WRAPPER
string(REGEX MATCH "\r?\n[\t ]*#define[ \t]+COOT_USE_WRAPPER[ \t]*\r?\n" COOT_USE_WRAPPER "${_bandicoot_CONFIG_CONTENTS}")
# COOT_USE_OPENCL
string(REGEX MATCH "\r?\n[\t ]*#if[\t ]+!defined[(]COOT_USE_OPENCL[)][\t ]*\r?\n[\t
]*#define[ \t]+COOT_USE_OPENCL[ \t]*\r?\n" COOT_USE_OPENCL "${_bandicoot_CONFIG_CONTENTS}")
# COOT_USE_CUDA
string(REGEX MATCH "\r?\n[\t ]*#if[\t ]+!defined[(]COOT_USE_CUDA[)][\t ]*\r?\n[\t
]*#define[ \t]+COOT_USE_CUDA[ \t]*\r?\n" COOT_USE_CUDA "${_bandicoot_CONFIG_CONTENTS}")
# COOT_USE_LAPACK
string(REGEX MATCH "\r?\n[\t ]*#if[\t ]+!defined[(]COOT_USE_LAPACK[)][\t ]*\r?\n[\t ]*#define[ \t]+COOT_USE_LAPACK[ \t]*\r?\n" COOT_USE_LAPACK "${_bandicoot_CONFIG_CONTENTS}")
# COOT_USE_BLAS
string(REGEX MATCH "\r?\n[\t ]*#if[\t ]+!defined[(]COOT_USE_BLAS[)][\t ]*\r?\n[\t ]*#define[ \t]+COOT_USE_BLAS[ \t]*\r?\n" COOT_USE_BLAS "${_bandicoot_CONFIG_CONTENTS}")
# If we aren't wrapping, things get a little more complex.
if(NOT COOT_USE_WRAPPER)
set(COOT_NEED_LIBRARY false)
message(STATUS "COOT_USE_WRAPPER is not defined, so all dependencies of "
"Bandicoot must be manually linked.")
set(HAVE_OPENCL false)
set(HAVE_CUDA false)
set(HAVE_LAPACK false)
set(HAVE_BLAS false)
# Search for OpenCL.
if (NOT "${COOT_USE_OPENCL}" STREQUAL "" AND NOT HAVE_OPENCL)
set(OpenCL_FIND_QUIETLY true)
include(FindOpenCL)
if (OpenCL_FOUND)
message(STATUS "OpenCL includes: ${OpenCL_INCLUDE_DIRS}")
message(STATUS "OpenCL libraries: ${OpenCL_LIBRARIES}")
set(SUPPORT_INCLUDE_DIRS "${SUPPORT_INCLUDE_DIRS}"
"${OpenCL_INCLUDE_DIRS}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${OpenCL_LIBRARIES}")
set(HAVE_OPENCL true)
endif ()
# Search for clBLAS.
set(CLBLAS_FIND_QUIETLY true)
include(COOT_FindCLBLAS)
if (CLBLAS_FOUND)
message(STATUS "clBLAS includes: ${CLBLAS_INCLUDE_DIR}")
message(STATUS "clBLAS libraries: ${CLBLAS_LIBRARIES}")
set(SUPPORT_INCLUDE_DIRS "${SUPPORT_INCLUDE_DIRS}"
"${CLBLAS_INCLUDE_DIR}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${CLBLAS_LIBRARIES}")
set(HAVE_CLBLAS true)
endif ()
endif ()
# Search for CUDA.
if (NOT COOT_USE_CUDA AND NOT HAVE_CUDA)
# FindCUDA is deprecated since version 3.10 and replaced with
# FindCUDAToolkit wich was added in CMake 3.17.
message(STATUS "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}")
if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS "3.67")
set(CUDA_FIND_QUIETLY true)
find_package(CUDA)
if (CUDA_FOUND)
message(STATUS "CUDA includes: ${CUDA_INCLUDE_DIRS}")
message(STATUS "CUDA libraries: ${CUDA_LIBRARIES}")
# We also need NVRTC and also libcuda itself, which the old FindCUDA package do not find.
find_library(CUDA_cuda_LIBRARY cuda
HINTS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_TOOLKIT_ROOT_DIR}/lib ${CUDA_TOOLKIT_ROOT_DIR}/lib64)
find_library(CUDA_nvrtc_LIBRARY nvrtc
HINTS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_TOOLKIT_ROOT_DIR}/lib ${CUDA_TOOLKIT_ROOT_DIR}/lib64)
include(COOT_FindNVRTC)
if (NVRTC_FOUND)
message(STATUS "NVRTC libraries: ${NVRTC_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${NVRTC_LIBRARIES}")
endif ()
set(SUPPORT_INCLUDE_DIRS "${SUPPORT_INCLUDE_DIRS}"
"${CUDA_INCLUDE_DIRS}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}"
"${CUDA_LIBRARIES}"
"${CUDA_nvrtc_LIBRARY}"
"${CUDA_CUDA_LIBRARY}"
"${CUDA_CUBLAS_LIBRARIES}"
"${CUDA_curand_LIBRARY}"
"${CUDA_cusolver_LIBRARY}")
set(CUDA_INCLUDE_DIRS "")
set(HAVE_CUDA true)
endif ()
else ()
set(CUDA_TOOLKIT_FIND_QUIETLY true)
find_package(CUDAToolkit REQUIRED)
if (CUDAToolkit_FOUND)
message(STATUS "CUDA includes: ${CUDAToolkit_INCLUDE_DIRS}")
message(STATUS "CUDA libraries: ${CUDAToolkit_LIBRARY_DIR}")
set(CUDA_LIBRARIES CUDA::cudart CUDA::cuda_driver)
set(CUDA_CUBLAS_LIBRARIES CUDA::cublas)
set(CUDA_curand_LIBRARY CUDA::curand)
set(CUDA_cusolver_LIBRARY CUDA::cusolver)
set(CUDA_nvrtc_LIBRARY CUDA::nvrtc)
set(SUPPORT_INCLUDE_DIRS "${SUPPORT_INCLUDE_DIRS}"
"${CUDAToolkit_INCLUDE_DIRS}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}"
CUDA_LIBRARIES
CUDA_CUBLAS_LIBRARIES
CUDA_curand_LIBRARY
CUDA_cusolver_LIBRARY
CUDA_nvrtc_LIBRARY)
set(HAVE_CUDA true)
endif()
endif ()
endif ()
# Search for LAPACK/BLAS (or replacement).
if ((NOT "${COOT_USE_LAPACK}" STREQUAL "") AND
(NOT "${COOT_USE_BLAS}" STREQUAL ""))
# In order of preference: MKL, ACML, OpenBLAS, ATLAS
set(MKL_FIND_QUIETLY true)
include(ARMA_FindMKL)
set(ACMLMP_FIND_QUIETLY true)
include(ARMA_FindACMLMP)
set(ACML_FIND_QUIETLY true)
include(ARMA_FindACML)
if (MKL_FOUND)
message(STATUS "Using MKL for LAPACK/BLAS: ${MKL_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${MKL_LIBRARIES}")
set(HAVE_LAPACK true)
set(HAVE_BLAS true)
elseif (ACMLMP_FOUND)
message(STATUS "Using multi-core ACML libraries for LAPACK/BLAS:
${ACMLMP_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${ACMLMP_LIBRARIES}")
set(HAVE_LAPACK true)
set(HAVE_BLAS true)
elseif (ACML_FOUND)
message(STATUS "Using ACML for LAPACK/BLAS: ${ACML_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${ACML_LIBRARIES}")
set(HAVE_LAPACK true)
set(HAVE_BLAS true)
endif ()
endif ()
# If we haven't found BLAS, try.
if (NOT "${COOT_USE_BLAS}" STREQUAL "" AND NOT HAVE_BLAS)
# Search for BLAS.
set(OpenBLAS_FIND_QUIETLY false)
include(ARMA_FindOpenBLAS)
set(CBLAS_FIND_QUIETLY true)
include(ARMA_FindCBLAS)
set(BLAS_FIND_QUIETLY true)
include(ARMA_FindBLAS)
if (OpenBLAS_FOUND)
# Warn if ATLAS is found also.
if (CBLAS_FOUND)
message(STATUS "Warning: both OpenBLAS and ATLAS have been found; "
"ATLAS will not be used.")
endif ()
message(STATUS "Using OpenBLAS for BLAS: ${OpenBLAS_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${OpenBLAS_LIBRARIES}")
set(HAVE_BLAS true)
elseif (CBLAS_FOUND)
message(STATUS "Using ATLAS for BLAS: ${CBLAS_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${CBLAS_LIBRARIES}")
set(SUPPORT_INCLUDE_DIRS "${SUPPORT_INCLUDE_DIRS}"
"${CBLAS_INCLUDE_DIR}")
set(HAVE_BLAS true)
elseif (BLAS_FOUND)
message(STATUS "Using standard BLAS: ${BLAS_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${BLAS_LIBRARIES}")
set(HAVE_BLAS true)
endif ()
endif ()
# If we haven't found LAPACK, try.
if (NOT "${COOT_USE_LAPACK}" STREQUAL "" AND NOT HAVE_LAPACK)
# Search for LAPACK.
set(CLAPACK_FIND_QUIETLY true)
include(ARMA_FindCLAPACK)
set(LAPACK_FIND_QUIETLY true)
include(ARMA_FindLAPACK)
# Only use ATLAS if OpenBLAS isn't being used.
if (CLAPACK_FOUND AND NOT OpenBLAS_FOUND)
message(STATUS "Using ATLAS for LAPACK: ${CLAPACK_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${CLAPACK_LIBRARIES}")
set(SUPPORT_INCLUDE_DIRS "${SUPPORT_INCLUDE_DIRS}"
"${CLAPACK_INCLUDE_DIR}")
set(HAVE_LAPACK true)
elseif (LAPACK_FOUND)
message(STATUS "Using standard LAPACK: ${LAPACK_LIBRARIES}")
set(SUPPORT_LIBRARIES "${SUPPORT_LIBRARIES}" "${LAPACK_LIBRARIES}")
set(HAVE_LAPACK true)
endif ()
endif ()
if (NOT "${COOT_USE_LAPACK}" STREQUAL "" AND NOT HAVE_LAPACK)
message(FATAL_ERROR "Cannot find LAPACK library, but COOT_USE_LAPACK is "
"set. Try specifying LAPACK libraries manually by setting the "
"LAPACK_LIBRARY variable.")
endif ()
if (NOT "${COOT_USE_BLAS}" STREQUAL "" AND NOT HAVE_BLAS)
message(FATAL_ERROR "Cannot find BLAS library, but COOT_USE_BLAS is set. "
"Try specifying BLAS libraries manually by setting the BLAS_LIBRARY "
"variable.")
endif ()
endif()
else()
message(STATUS "${BANDICOOT_INCLUDE_DIR}/bandicoot_bits/config.hpp not "
"found! Cannot determine what to link against.")
endif()
if (COOT_NEED_LIBRARY)
# UNIX paths are standard, no need to write.
find_library(BANDICOOT_LIBRARY
NAMES bandicoot
PATHS "$ENV{ProgramFiles}/Bandicoot/lib" "$ENV{ProgramFiles}/Bandicoot/lib64" "$ENV{ProgramFiles}/Bandicoot"
)
# Checks 'REQUIRED', 'QUIET' and versions.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Bandicoot
REQUIRED_VARS BANDICOOT_LIBRARY BANDICOOT_INCLUDE_DIR
VERSION_VAR BANDICOOT_VERSION_STRING)
else ()
# Checks 'REQUIRED', 'QUIET' and versions.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Bandicoot
REQUIRED_VARS BANDICOOT_INCLUDE_DIR
VERSION_VAR BANDICOOT_VERSION_STRING)
endif ()
if (BANDICOOT_FOUND)
# Also include support include directories.
set(BANDICOOT_INCLUDE_DIRS ${BANDICOOT_INCLUDE_DIR} ${SUPPORT_INCLUDE_DIRS})
# Also include support libraries to link against.
if (COOT_NEED_LIBRARY)
set(BANDICOOT_LIBRARIES ${BANDICOOT_LIBRARY} ${SUPPORT_LIBRARIES})
else ()
set(BANDICOOT_LIBRARIES ${SUPPORT_LIBRARIES})
endif ()
message(STATUS "Bandicoot libraries: ${BANDICOOT_LIBRARIES}")
message(STATUS "Bandicoot includes: ${BANDICOOT_INCLUDE_DIR}")
endif ()
# Hide internal variables
mark_as_advanced(
BANDICOOT_INCLUDE_DIR
BANDICOOT_LIBRARIES)
if (BANDICOOT_FOUND AND NOT TARGET Bandicoot::Bandicoot)
add_library(Bandicoot::Bandicoot INTERFACE IMPORTED)
set_target_properties(Bandicoot::Bandicoot PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${BANDICOOT_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${BANDICOOT_LIBRARIES}")
endif()
+75
View File
@@ -0,0 +1,75 @@
# ==================================================================================================
# This file is part of the CLBlast project. The project is licensed under Apache Version 2.0. This
# project loosely follows the Google C++ styleguide and uses a tab-size of two spaces and a max-
# width of 100 characters per line.
#
# Author(s):
# Cedric Nugteren <www.cedricnugteren.nl>
#
# ==================================================================================================
#
# Defines the following variables:
# CBLAS_FOUND Boolean holding whether or not the Netlib BLAS library was found
# CBLAS_INCLUDE_DIRS The Netlib BLAS include directory
# CBLAS_LIBRARIES The Netlib BLAS library
#
# In case BLAS is not installed in the default directory, set the CBLAS_ROOT variable to point to
# the root of BLAS, such that 'cblas.h' can be found in $CBLAS_ROOT/include. This can either be
# done using an environmental variable (e.g. export CBLAS_ROOT=/path/to/BLAS) or using a CMake
# variable (e.g. cmake -DCBLAS_ROOT=/path/to/BLAS ..).
#
# ==================================================================================================
# Sets the possible install locations
set(CBLAS_HINTS
${CBLAS_ROOT}
$ENV{CBLAS_ROOT}
)
set(CBLAS_PATHS
/usr
/usr/local
/usr/local/opt
/System/Library/Frameworks
)
# Finds the include directories
find_path(CBLAS_INCLUDE_DIRS
NAMES cblas.h
HINTS ${CBLAS_HINTS}
PATH_SUFFIXES
include inc include/x86_64 include/x64
openblas/include include/blis blis/include blis/include/blis
Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/Versions/Current/Headers
PATHS ${CBLAS_PATHS}
DOC "Netlib BLAS include header cblas.h"
)
mark_as_advanced(CBLAS_INCLUDE_DIRS)
# Finds the library
find_library(CBLAS_LIBRARIES
NAMES cblas blas blis openblas accelerate
HINTS ${CBLAS_HINTS}
PATH_SUFFIXES
lib lib64 lib/x86_64 lib/x64 lib/x86 lib/Win32 lib/import lib64/import
openblas/lib blis/lib lib/atlas-base
PATHS ${CBLAS_PATHS}
DOC "Netlib BLAS library"
)
mark_as_advanced(CBLAS_LIBRARIES)
# ==================================================================================================
# Notification messages
if(NOT CBLAS_INCLUDE_DIRS)
message(STATUS "Could NOT find 'cblas.h', install a CPU Netlib BLAS or set CBLAS_ROOT")
endif()
if(NOT CBLAS_LIBRARIES)
message(STATUS "Could NOT find a CPU Netlib BLAS library, install it or set CBLAS_ROOT")
endif()
# Determines whether or not BLAS was found
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CBLAS DEFAULT_MSG CBLAS_INCLUDE_DIRS CBLAS_LIBRARIES)
# ==================================================================================================
+27 -11
View File
@@ -1,19 +1,26 @@
# ensmallen CMake configuration. This project has no configurable options---it
# just installs the headers to the install location, and optionally builds the
# test program.
cmake_minimum_required(VERSION 3.3.2)
project(ensmallen
cmake_minimum_required(VERSION 3.5...4.0)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release") # ensure the tests are built with optimisation
endif ()
project(ensmallen
LANGUAGES C CXX)
# Configurable options for CMake.
option(USE_OPENMP "If available, use OpenMP for parallelization." ON)
option(BUILD_TESTS "Build tests." ON)
option(USE_BANDICOOT "If available, build against Bandicoot for GPU support." ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake")
# Set required C++ standard to C++11.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Set minimum required C++ standard to C++14.
if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
elseif (${CMAKE_CXX_STANDARD} LESS 14)
message(FATAL_ERROR "ensmallen requires C++14 or newer!")
endif ()
# Extract version from sources.
set(ENSMALLEN_VERSION_FILE_NAME "${PROJECT_SOURCE_DIR}/include/ensmallen_bits/ens_version.hpp")
@@ -40,7 +47,7 @@ target_include_directories(ensmallen INTERFACE
if(MSVC)
target_compile_options(ensmallen INTERFACE $<BUILD_INTERFACE:/Wall>)
else()
target_compile_options(ensmallen INTERFACE $<BUILD_INTERFACE:-Wall -Wpedantic -Wunused-parameter>)
target_compile_options(ensmallen INTERFACE $<BUILD_INTERFACE:-Wall -Wpedantic -Wunused>)
endif()
# Find OpenMP and link it.
@@ -57,8 +64,19 @@ if(USE_OPENMP)
target_link_libraries(ensmallen INTERFACE OpenMP::OpenMP_CXX)
endif()
if(USE_BANDICOOT)
# Find Bandicoot and link it.
find_package(Bandicoot 2.1.0)
if(BANDICOOT_FOUND)
target_link_libraries(ensmallen INTERFACE Bandicoot::Bandicoot)
target_include_directories(ensmallen INTERFACE ${BANDICOOT_INCLUDE_DIR})
add_definitions(-DENS_USE_COOT)
endif()
endif()
# Find Armadillo and link it.
find_package(Armadillo 8.400.0 REQUIRED)
find_package(Armadillo 10.8.2 REQUIRED)
target_link_libraries(ensmallen INTERFACE Armadillo::Armadillo)
# Set helper variables for creating the version, config and target files.
@@ -96,6 +114,4 @@ install(FILES ${CMAKE_SOURCE_DIR}/include/ensmallen.hpp
# Enable testing and build tests.
enable_testing()
if (BUILD_TESTS)
add_subdirectory(tests)
endif()
add_subdirectory(tests)
+23 -5
View File
@@ -8,9 +8,27 @@ you have an improvement you would like to see, we would love to include it!
The ensmallen maintainer community overlaps heavily with the
[mlpack](https://github.com/mlpack/mlpack) community, so development discussions
can happen either here on Github, on the [mlpack mailing
list](http://lists.mlpack.org/mailman/listinfo/mlpack), or in the #mlpack
IRC channel on irc.freenode.net.
can happen either here on Github or in the `#mlpack:matrix.org` channel on
[Matrix](https://www.matrix.org/). See
[here](https://www.mlpack.org/doc/developer/community.html) for more
information.
## Usage of LLMs / Coding Assistants
Each contributor needs to understand the content they are proposing for ensmallen,
and how the proposed content changes and/or expands functionality in ensmallen.
Content includes source code, documentation, and other material such as images
and datasets. The pull request must justify the proposed content in the
description. Using coding assistants or tools like Large Language Models (LLMs)
does not grant additional privileges or reduce our expectations.
Each contributor is responsible for the proposed content, regardless of where
the content came from. The responsibility includes ensuring that the content
can be submitted to ensmallen and does not violate intellectual property rights
such as copyright(s). Source code in ensmallen is licensed under the BSD license;
see `LICENSE.txt` for details.
## Pull request process
Once a pull request is submitted, it must be reviewed and approved before a
merge, to ensure that:
@@ -108,8 +126,8 @@ $ cd ensmallen
# - or -
$ wget http://ensmallen.org/files/ensmallen-2.16.2.tar.gz
$ tar -xvzpf ensmallen-2.16.2.tar.gz
$ wget http://ensmallen.org/files/ensmallen-3.11.0.tar.gz
$ tar -xvzpf ensmallen-3.11.0.tar.gz
$ cd ensmallen-latest
```
+3 -3
View File
@@ -7,10 +7,10 @@ Source:
Files: *
Copyright:
Copyright 2008-2018, Ryan Curtin <ryan@ratml.org>
Copyright 2008-2023, Ryan Curtin <ryan@ratml.org>
Copyright 2008-2012, Dongryeol Lee <dongryel@cc.gatech.edu>
Copyright 2010-2012, James Cline <james.cline@gatech.edu>
Copyright 2013-2018, Marcus Edel <marcus.edel@fu-berlin.de>
Copyright 2013-2023, Marcus Edel <marcus.edel@fu-berlin.de>
Copyright 2013-2018, Sumedh Ghaisas <sumedhghaisas@gmail.com>
Copyright 2013, Mudit Raj Gupta <mudit.raaj.gupta@gmail.com>
Copyright 2014, Ryan Birmingham <birm@gatech.edu>
@@ -33,7 +33,7 @@ Copyright:
Copyright 2018, B Kartheek Reddy <bkartheekreddy@gmail.com>
Copyright 2018, Moksh Jain <mokshjn00@gmail.com>
Copyright 2018, Shikhar Jaiswal <jaiswalshikhar87@gmail.com>
Copyright 2018, Conrad Sanderson
Copyright 2018-2023, Conrad Sanderson
Copyright 2018, Dan Timson
Copyright 2019, Rahul Ganesh Prabhu
Copyright 2019, Roberto Hueso <robertohueso96@gmail.com>
+248
View File
@@ -1,3 +1,251 @@
### ensmallen ?.??.?: "???"
###### ????-??-??
### ensmallen 3.11.0: "Sunny Day"
###### 2025-12-15
* Refactor `GradientDescent` into
`GradientDescentType<UpdatePolicyType, DecayPolicyType>` and
add the `DeltaBarDelta` and `MomentumDeltaBarDelta` optimizers
([#440](https://github.com/mlpack/ensmallen/pull/440)).
* Fix an off-by-one bug where the actual number of executed iterations was one
fewer than the specified `maxIterations`
([#443](https://github.com/mlpack/ensmallen/pull/443)).
### ensmallen 3.10.0: "Unexpected Rain"
###### 2025-09-25
* SGD-like optimizers now all divide the step size by the batch size so that
step sizes don't need to be tuned in addition to batch sizes. If you require
behavior from ensmallen 2, define the `ENS_OLD_SEPARABLE_STEP_BEHAVIOR` macro
before including `ensmallen.hpp`
([#431](https://github.com/mlpack/ensmallen/pull/431)).
* Remove deprecated `ParetoFront()` and `ParetoSet()` from multi-objective
optimizers ([#435](https://github.com/mlpack/ensmallen/pull/435)). Instead,
pass objects to the `Optimize()` function; see the documentation for each
multi-objective optimizer for more details. A typical transition will change
code like:
```c++
optimizer.Optimize(objectives, coordinates);
arma::cube paretoFront = optimizer.ParetoFront();
arma::cube paretoSet = optimizer.ParetoSet();
```
to instead gather the Pareto front and set in the call:
```c++
arma::cube paretoFront, paretoSet;
optimizer.Optimize(objectives, coordinates, paretoFront, paretoSet);
```
* Remove deprecated constructor for Active CMA-ES that takes `lowerBound` and
`upperBound` ([#435](https://github.com/mlpack/ensmallen/pull/435)).
Instead, pass an instantiated `BoundaryBoxConstraint` to the constructor. A
typical transition will change code like:
```c++
ActiveCMAES<FullSelection, BoundaryBoxConstraint> opt(lambda,
lowerBound, upperBound, ...);
```
into
```c++
ActiveCMAES<FullSelection, BoundaryBoxConstraint> opt(lambda,
BoundaryBoxConstraint(lowerBound, upperBound), ...);
```
* Add proximal gradient optimizers for L1-constrained and other related
problems: `FBS`, `FISTA`, and `FASTA`
([#427](https://github.com/mlpack/ensmallen/pull/427)). See the
documentation for more details.
* The `Lambda()` and `Sigma()` functions of the `AugLagrangian` optimizer,
which could be used to retrieve the Lagrange multipliers and penalty
parameter after optimization, are now deprecated
([#439](https://github.com/mlpack/ensmallen/pull/439)). Instead, pass a
vector and a double to the `Optimize()` function directly:
```c++
augLag.Optimize(function, coordinates, lambda, sigma)
```
and these will be filled with the final Lagrange multiplier estimates and
penalty parameters.
### ensmallen 2.22.2: "E-Bike Excitement"
###### 2025-04-30
* Fix include statement in `tests/de_test.cpp`
([#419](https://github.com/mlpack/ensmallen/pull/419)).
* Fix `exactObjective` output for SGD-like optimizers when the number of
iterations is an even number of epochs
([#417](https://github.com/mlpack/ensmallen/pull/417)).
* Increase tolerance in `demon_sgd_test.cpp`
([#420](https://github.com/mlpack/ensmallen/pull/420)).
* Set cmake version range to 3.5...4.0
([#422](https://github.com/mlpack/ensmallen/pull/422)).
### ensmallen 2.22.1: "E-Bike Excitement"
###### 2024-12-02
* Remove unused variables to fix compiler warnings
([#413](https://github.com/mlpack/ensmallen/pull/413)).
### ensmallen 2.22.0: "E-Bike Excitement"
###### 2024-11-29
* Update to C++14 standard
([#400](https://github.com/mlpack/ensmallen/pull/400)).
* Bump minimum Armadillo version to 10.8
([#404](https://github.com/mlpack/ensmallen/pull/404)).
* For Armadillo 14.2.0 switch to `.index_min()` and `.index_max()`
([#409](https://github.com/mlpack/ensmallen/pull/409)).
* Added IPOP and BIPOP restart mechanisms for CMA-ES.
([#403](https://github.com/mlpack/ensmallen/pull/403)).
### ensmallen 2.21.1: "Bent Antenna"
###### 2024-02-15
* Fix numerical precision issues for small-gradient L-BFGS scaling factor
computations ([#392](https://github.com/mlpack/ensmallen/pull/392)).
* Ensure the tests are built with optimisation enabled
([#394](https://github.com/mlpack/ensmallen/pull/394)).
### ensmallen 2.21.0: "Bent Antenna"
###### 2023-11-27
* Clarify return values for different callback types
([#383](https://github.com/mlpack/ensmallen/pull/383)).
* Fix return types of callbacks
([#382](https://github.com/mlpack/ensmallen/pull/382)).
* Minor cleanup for printing optimization reports via `Report()`
([#385](https://github.com/mlpack/ensmallen/pull/385)).
### ensmallen 2.20.0: "Stripped Bolt Head"
###### 2023-10-02
* Implementation of Active CMAES
([#367](https://github.com/mlpack/ensmallen/pull/367)).
* LBFGS: avoid generation of NaNs, and add checks for finite values
([#368](https://github.com/mlpack/ensmallen/pull/368)).
* Fix CNE test tolerances
([#360](https://github.com/mlpack/ensmallen/pull/360)).
* Rename `SCD` optimizer, to `CD`
([#379](https://github.com/mlpack/ensmallen/pull/379)).
### ensmallen 2.19.1: "Eight Ball Deluxe"
###### 2023-01-30
* Avoid deprecation warnings in Armadillo 11.2+
([#347](https://github.com/mlpack/ensmallen/pull/347)).
### ensmallen 2.19.0: "Eight Ball Deluxe"
###### 2022-04-06
* Added DemonSGD and DemonAdam optimizers
([#211](https://github.com/mlpack/ensmallen/pull/211)).
* Fix bug with Adam-like optimizers not resetting when `resetPolicy` is `true`.
([#340](https://github.com/mlpack/ensmallen/pull/340)).
* Add Yogi optimizer
([#232](https://github.com/mlpack/ensmallen/pull/232)).
* Add AdaBelief optimizer
([#233](https://github.com/mlpack/ensmallen/pull/233)).
* Add AdaSqrt optimizer
([#234](https://github.com/mlpack/ensmallen/pull/234)).
* Bump check for minimum supported version of Armadillo
([#342](https://github.com/mlpack/ensmallen/pull/342)).
### ensmallen 2.18.2: "Fairmount Bagel"
###### 2022-02-13
* Update Catch2 to 2.13.8
([#336](https://github.com/mlpack/ensmallen/pull/336)).
* Fix epoch timing output
([#337](https://github.com/mlpack/ensmallen/pull/337)).
### ensmallen 2.18.1: "Fairmount Bagel"
###### 2021-11-19
* Accelerate SGD test time
([#330](https://github.com/mlpack/ensmallen/pull/300)).
* Fix potential infinite loop in CMAES
([#331](https://github.com/mlpack/ensmallen/pull/331)).
* Fix SCD partial gradient test
([#332](https://github.com/mlpack/ensmallen/pull/332)).
### ensmallen 2.18.0: "Fairmount Bagel"
###### 2021-10-20
* Add gradient value clipping and gradient norm scaling callback
([#315](https://github.com/mlpack/ensmallen/pull/315)).
* Remove superfluous CMake option to build the tests
([#313](https://github.com/mlpack/ensmallen/pull/313)).
* Bump minimum Armadillo version to 9.800
([#318](https://github.com/mlpack/ensmallen/pull/318)).
* Update Catch2 to 2.13.7
([#322](https://github.com/mlpack/ensmallen/pull/322)).
* Remove redundant template argument for C++20 compatibility
([#324](https://github.com/mlpack/ensmallen/pull/324)).
* Fix MOEAD test stability
([#327](https://github.com/mlpack/ensmallen/pull/327)).
### ensmallen 2.17.0: "Pachis Din Me Pesa Double"
###### 2021-07-06
* CheckArbitraryFunctionTypeAPI extended for MOO support
([#283](https://github.com/mlpack/ensmallen/pull/283)).
* Refactor NSGA2
([#263](https://github.com/mlpack/ensmallen/pull/263),
[#304](https://github.com/mlpack/ensmallen/pull/304)).
* Add Indicators for Multiobjective optimizers
([#285](https://github.com/mlpack/ensmallen/pull/285)).
* Make Callback flexible for MultiObjective Optimizers
([#289](https://github.com/mlpack/ensmallen/pull/289)).
* Add ZDT Test Suite
([#273](https://github.com/mlpack/ensmallen/pull/273)).
* Add MOEA-D/DE Optimizer
([#269](https://github.com/mlpack/ensmallen/pull/269)).
* Introduce Policy Methods for MOEA/D-DE
([#293](https://github.com/mlpack/ensmallen/pull/293)).
* Add Das-Dennis weight initialization method
([#295](https://github.com/mlpack/ensmallen/pull/295)).
* Add Dirichlet Weight Initialization
([#296](https://github.com/mlpack/ensmallen/pull/296)).
* Improved installation and compilation instructions
([#300](https://github.com/mlpack/ensmallen/pull/300)).
* Disable building the tests by default for faster installation
([#303](https://github.com/mlpack/ensmallen/pull/303)).
* Modify matrix initialisation to take into account
default element zeroing in Armadillo 10.5
([#305](https://github.com/mlpack/ensmallen/pull/305)).
### ensmallen 2.16.2: "Severely Dented Can Of Polyurethane"
###### 2021-03-24
* Fix CNE test trials
+77 -38
View File
@@ -2,33 +2,35 @@
<a href="http://ensmallen.org/"><img src="http://ensmallen.org/img/ensmallen_text.svg" style="background-color:rgba(0,0,0,0);" height=230 alt="ensmallen: a C++ header-only library for numerical optimization"></a>
</h2>
**ensmallen** is a C++ header-only library for numerical optimization.
**ensmallen** is a high-quality C++ library for non-linear numerical optimization.
Documentation and downloads: http://ensmallen.org
ensmallen provides a simple set of abstractions for writing an objective
function to optimize. It also provides a large set of standard and cutting-edge
optimizers that can be used for virtually any numerical optimization task.
These include full-batch gradient descent techniques, small-batch techniques,
gradient-free optimizers, and constrained optimization.
ensmallen provides many types of optimizers that can be used
for virtually any numerical optimization task.
This includes gradient descent techniques, gradient-free optimizers,
and constrained optimization.
Examples include L-BFGS, SGD, CMAES and Simulated Annealing.
ensmallen also allows optional callbacks to customize the optimization process.
Documentation and downloads: https://ensmallen.org
### Requirements
* C++ compiler with C++11 support
* Armadillo: http://arma.sourceforge.net
* C++ compiler with C++14 support
* Armadillo (https://arma.sourceforge.net), version 10.8.2 or later
* OpenBLAS or Intel MKL or LAPACK (see Armadillo site for details)
### Installation
ensmallen can be installed with CMake 3.3 or later.
If CMake is not already available on your system, it can be obtained from https://cmake.org
ensmallen can be installed in several ways: either manually or via cmake,
with or without root access.
If you are using an older system such as RHEL 7 or CentOS 7,
an updated version of CMake is also available via the EPEL repository via the `cmake3` package.
The cmake based installation will check the requirements
and optionally build the tests. If cmake 3.3 (or a later version)
is not already available on your system, it can be obtained
from [cmake.org](https://cmake.org).
Example installation:
Example cmake based installation with root access:
```
mkdir build
@@ -37,10 +39,54 @@ cmake ..
sudo make install
```
Example cmake based installation without root access,
installing into `/home/blah/` (adapt as required):
### Example Usage
```
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/home/blah/
make install
```
See [`example.cpp`](example.cpp) for example usage of the L-BFGS optimizer in a linear regression setting.
The above will create a directory named `/home/blah/include/`
and place all ensmallen headers there.
To optionally build and run the tests
(after running cmake as above),
use the following additional commands:
```
make ensmallen_tests
./ensmallen_tests --durations yes
```
Manual installation involves simply copying the `include/ensmallen.hpp` header
***and*** the associated `include/ensmallen_bits` directory to a location
such as `/usr/include/` which is searched by your C++ compiler.
If you can't use `sudo` or don't have write access to `/usr/include/`,
use a directory within your own home directory (eg. `/home/blah/include/`).
### Example Compilation
If you have installed ensmallen in a standard location such as `/usr/include/`:
g++ prog.cpp -o prog -O2 -larmadillo
If you have installed ensmallen in a non-standard location,
such as `/home/blah/include/`, you will need to make sure
that your C++ compiler searches `/home/blah/include/`
by explicitly specifying the directory as an argument/option.
For example, using the `-I` switch in gcc and clang:
g++ prog.cpp -o prog -O2 -I /home/blah/include/ -larmadillo
### Example Optimization
See [`example.cpp`](example.cpp) for example usage of the L-BFGS optimizer
in a linear regression setting.
### License
@@ -48,7 +94,7 @@ See [`example.cpp`](example.cpp) for example usage of the L-BFGS optimizer in a
Unless stated otherwise, the source code for **ensmallen** is licensed under the
3-clause BSD license (the "License"). A copy of the License is included in the
"LICENSE.txt" file. You may also obtain a copy of the License at
http://opensource.org/licenses/BSD-3-Clause .
http://opensource.org/licenses/BSD-3-Clause
### Citation
@@ -57,28 +103,20 @@ Please cite the following paper if you use ensmallen in your research and/or
software. Citations are useful for the continued development and maintenance of
the library.
* S. Bhardwaj, R. Curtin, M. Edel, Y. Mentekidis, C. Sanderson.
[ensmallen: a flexible C++ library for efficient function optimization](http://www.ensmallen.org/files/ensmallen_2018.pdf).
Workshop on Systems for ML and Open Source Software at NIPS 2018.
* Ryan R. Curtin, Marcus Edel, Rahul Ganesh Prabhu, Suryoday Basak, Zhihao Lou, Conrad Sanderson.
[The ensmallen library for flexible numerical optimization](https://jmlr.org/papers/volume22/20-416/20-416.pdf).
Journal of Machine Learning Research, Vol. 22, No. 166, 2021.
```
@article{DBLP:journals/corr/abs-1810-09361,
author = {Shikhar Bhardwaj and
Ryan R. Curtin and
Marcus Edel and
Yannis Mentekidis and
Conrad Sanderson},
title = {ensmallen: a flexible {C++} library for efficient function optimization},
journal = {CoRR},
volume = {abs/1810.09361},
doi = {10.5281/zenodo.2008650},
year = {2018},
url = {http://arxiv.org/abs/1810.09361},
archivePrefix = {arXiv},
eprint = {1810.09361},
timestamp = {Wed, 31 Oct 2018 14:24:29 +0100},
biburl = {https://dblp.org/rec/bib/journals/corr/abs-1810-09361},
bibsource = {dblp computer science bibliography, https://dblp.org}
@article{ensmallen_JMLR_2021,
author = {Ryan R. Curtin and Marcus Edel and Rahul Ganesh Prabhu and Suryoday Basak and Zhihao Lou and Conrad Sanderson},
title = {The ensmallen library for flexible numerical optimization},
journal = {Journal of Machine Learning Research},
year = {2021},
volume = {22},
number = {166},
pages = {1--6},
url = {http://jmlr.org/papers/v22/20-416.html}
}
```
@@ -111,3 +149,4 @@ the library.
* N Rajiv Vaidyanathan
* Roberto Hueso
* Sayan Goswami
+357 -96
View File
@@ -1,10 +1,10 @@
Callbacks in ensmallen are methods that are called at various states during the
optimization process, which can be used to implement and control behaviors such
as:
Callbacks in ensmallen are methods that are called at various stages of the
optimization process. These can be used to print information about the optimization, modify behavior of the optimization, or a wide range of other possibilities. Some examples of what callbacks can be used for include:
* Changing the learning rate.
* Printing of the current objective.
* Sending a message when the optimization hits a specific state such us a minimal objective.
* Printing the current objective.
* Sending a message when the optimization hits a specific state such us a
minimal objective.
Callbacks can be passed as an argument to the `Optimize()` function:
@@ -73,6 +73,27 @@ std::cout << callback.BestObjective() << std::endl;
</details>
Numerous implemented and ready-to-use callbacks are included with ensmallen, and
it is also easy to write a custom callback.
* [`EarlyStopAtMinLoss`](#earlystopatminloss): stop the optimization if no
improvement has been made
* [`GradClipByNorm`](#gradclipbynorm): reduce the norm of the gradient to
prevent the exploding gradient problem
* [`GradClipByValue`](#gradclipbyvalue): clip the gradient to specified minimum
and maximum values
* [`PrintLoss`](#printloss): print the objective at each iteration to a
specified stream
* [`ProgressBar`](#progressbar): print a progress bar to the screen at each
iteration
* [`Report`](#report): print a report at the end of optimization
* [`StoreBestCoordinates`](#storebestcoordinates): store the coordinates that
give the best objective value at the end of an epoch
* [`TimerStop`](#timerstop): stop the optimization after a given amount of time
A [guide for implementing custom callbacks](#custom-callbacks) is below, and a
few [example custom callbacks](#custom-callback-examples) are given too.
## Built-in Callbacks
### EarlyStopAtMinLoss
@@ -136,7 +157,7 @@ EarlyStopAtMinLoss cb(
// You could also, e.g., print the validation loss here to watch it converge.
return lrfValidation.Evaluate(coordinates);
});
arma::mat coordinates = lrfTrain.GetInitialPoint();
SMORMS3 smorms3;
smorms3.Optimize(lrfTrain, coordinates, cb);
@@ -144,6 +165,82 @@ smorms3.Optimize(lrfTrain, coordinates, cb);
</details>
### Gradient Clipping
One challenge in optimization is dealing with "exploding gradients", where large
parameter gradients can cause the optimizer to make excessively large updates,
potentially pushing the model into regions of high loss or causing numerical
instability. This can happen due to:
* A high learning rate, leading to large gradient updates.
* Poorly scaled datasets, resulting in significant variance between data points.
* A loss function that generates disproportionately large error values.
Common solutions for this problem are:
#### GradClipByNorm
In this method, the solution is to change the derivative
of the error before applying the update step. One option is to clip the norm
`||g||` of the gradient `g` before a parameter update. So given the gradient,
and a maximum norm value, the callback normalizes the gradient so that its
L2-norm is less than or equal to the given maximum norm value.
#### Constructors
* `GradClipByNorm(`_`maxNorm`_`)`
#### Attributes
| **type** | **name** | **description** | **default** |
|----------|----------|-----------------|-------------|
| `double` | **`maxNorm`** | The maximum clipping value. | |
#### Examples:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
AdaDelta optimizer(1.0, 1, 0.99, 1e-8, 1000, 1e-9, true);
RosenbrockFunction f;
arma::mat coordinates = f.GetInitialPoint();
optimizer.Optimize(f, coordinates, GradClipByNorm(0.3));
```
#### GradClipByValue
In this method, the solution is to change the derivative
of the error before applying the update step. One option is to clip the
parameter gradient element-wise before a parameter update.
#### Constructors
* `GradClipByValue(`_`min, max`_`)`
#### Attributes
| **type** | **name** | **description** | **default** |
|----------|----------|-----------------|-------------|
| `double` | **`min`** | The minimum value to clip to. | |
| `double` | **`max`** | The maximum value to clip to. | |
#### Examples:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
AdaDelta optimizer(1.0, 1, 0.99, 1e-8, 1000, 1e-9, true);
RosenbrockFunction f;
arma::mat coordinates = f.GetInitialPoint();
optimizer.Optimize(f, coordinates, GradClipByValue(0, 1.3));
```
### PrintLoss
Callback that prints loss to stdout or a specified output stream.
@@ -208,7 +305,7 @@ optimizer.Optimize(f, coordinates, ProgressBar());
</details>
### Report
### Report
Callback that prints a optimizer report to stdout or a specified output stream.
@@ -336,143 +433,304 @@ std::cout << "The optimized model found by AdaDelta has the "
</details>
## Callback States
### TimerStop
Callbacks are called at several states during the optimization process:
Callback that stops optimization after a certain amount of time has elapsed.
* At the beginning and end of the optimization process.
* After any call to `Evaluate()` and `EvaluateConstraint`.
* After any call to `Gradient()` and `GradientConstraint`.
* At the start and end of an epoch.
#### Constructors
Each callback provides optimization relevant information that can be accessed or
modified.
* `TimerStop(`_`seconds`_`)`
#### Examples:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
AdaDelta optimizer(1.0, 1, 0.99, 1e-8, 1000, 1e-9, true);
RosenbrockFunction f;
arma::mat coordinates = f.GetInitialPoint();
// Limit optimization to 15 seconds.
optimizer.Optimize(f, coordinates, TimerStop(15));
```
</details>
## Custom Callbacks
Custom callbacks can be easily implemented by creating a class and simply
implementing functions for each individual callback that you are interested in
handling, plus any other functionality you might need (e.g. constructors,
accessors).
Thus, when writing a custom callback, start with an empty class like this:
```c++
class CustomCallback
{
public:
// Add individual callback handlers that you are interested in handling!
};
```
and add any of the individual callback handler functions described below.
### BeginOptimization
Called at the beginning of the optimization process.
Called at the beginning of the optimization process. Add this function to your
callback class with your desired implementation:
* `BeginOptimization(`_`optimizer, function, coordinates`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType>
void BeginOptimization(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates);
```
#### Attributes
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function to be optimized (e.g. the first argument given to
`optimizer.Optimize()`.
* `coordinates`: the current coordinates for optimization; since optimization
is just beginning, this is the exact same matrix given to `Optimize()`.
### EndOptimization
Called at the end of the optimization process.
Called at the end of the optimization process. Add this function to your
callback class with your desired implementation:
* `EndOptimization(`_`optimizer, function, coordinates`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType>
void EndOptimization(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates);
```
#### Attributes
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that has been optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the final coordinates for optimization; since optimization is
ending, these are the same values that will be in the resulting matrix after
`Optimize()` finishes.
### Evaluate
Called after any call to `Evaluate()`.
Called after any call to `Evaluate()` or `EvaluateWithGradient()`. Add this
function to your callback class with your desired implementation:
* `Evaluate(`_`optimizer, function, coordinates, objective`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType>
bool Evaluate(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const double objective);
```
#### Attributes
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that is being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates with which `function.Evaluate()` was called.
* `objective`: the result of `function.Evaluate(coordinates)`.
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
| `double` | **`objective`** | Objective value of the current point. |
If the callback returns `true`, the optimization will be terminated.
### EvaluateConstraint
Called after any call to `EvaluateConstraint()`.
Called after any call to `EvaluateConstraint()`, for
[constrained functions](#constrained-functions). Add this function to your
callback class with your desired implementation:
* `EvaluateConstraint(`_`optimizer, function, coordinates, constraint, constraintValue`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType>
bool EvaluateConstraint(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraintIndex,
const double constraintValue);
```
#### Attributes
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that is being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates with which `function.EvaluateConstraint()` was
called.
* `constraintIndex`: the index of the constraint that was evaluated
* `constraintValue`: the result of
`function.EvaluateConstraint(coordinates, constraintIndex)`.
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
| `size_t` | **`constraint`** | The index of the constraint. |
| `double` | **`constraintValue`** | Constraint value of the current point. |
If the callback returns `true`, the optimization will be terminated.
### Gradient
Called after any call to `Gradient()`.
Called after any call to `Gradient()` or `EvaluateWithGradient()`. Add this
function to your callback class with your desired implementation:
* `Gradient(`_`optimizer, function, coordinates, gradient`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
bool Gradient(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
GradType& gradient);
```
#### Attributes
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that is being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates with which `function.Gradient()` was called.
* `gradient`: the computed gradient (can be modified!).
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
| `GradType` | **`gradient`** | Matrix that holds the gradient. |
If the callback returns `true`, the optimization will be terminated.
### GradientConstraint
Called after any call to `GradientConstraint()`.
Called after any call to `GradientConstraint()` for
[constrained functions](#constrained-functions). Add this function to your
callback class with your desired implementation:
* `GradientConstraint(`_`optimizer, function, coordinates, constraint, gradient`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
bool GradientConstraint(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraintIndex,
GradType& constraintGradient);
```
#### Attributes
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that is being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates with which `function.GradientConstraint()` was
called.
* `constraintIndex`: the index of the constraint whose gradient was computed.
* `constraintGradient`: the computed result of
`function.GradientConstraint()`.
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
| `size_t` | **`constraint`** | The index of the constraint. |
| `GradType` | **`gradient`** | Matrix that holds the gradient. |
If the callback returns `true`, the optimization will be terminated.
### BeginEpoch
Called at the beginning of a pass over the data. The objective may be exact or
an estimate depending on `exactObjective` value.
Called at the beginning of a pass over the data, for
[separable functions](#separable-functions). The objective may be exact or
an estimate depending on the optimizer's `ExactObjective()` value. Add this
function to your callback class with your desired implementation:
* `BeginEpoch(`_`optimizer, function, coordinates, epoch, objective`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType>
bool BeginEpoch(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective);
```
#### Attributes
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that is being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates at the start of the epoch.
* `epoch`: the epoch number.
* `objective`: the exact or approximate objective at the end of the previous
epoch.
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
| `size_t` | **`epoch`** | The index of the current epoch. |
| `double` | **`objective`** | Objective value of the current point. |
If the callback returns `true`, the optimization will be terminated.
### EndEpoch
Called at the end of a pass over the data. The objective may be exact or
an estimate depending on `exactObjective` value.
Called at the end of a pass over the data, for
[separable functions](#separable-functions). The objective may be exact or an
estimate depending on the optimizer's `ExactObjective()` value. Add this
function to your callback class with your desired implementation:
* `EndEpoch(`_`optimizer, function, coordinates, epoch, objective`_`)`
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType>
bool EndEpoch(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective);
```
#### Attributes
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that is being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates at the end of the epoch.
* `epoch`: the epoch number.
* `objective`: the exact or approximate objective at the end of the epoch.
| **type** | **name** | **description** |
|----------|----------|-----------------|
| `OptimizerType` | **`optimizer`** | The optimizer used to update the function. |
| `FunctionType` | **`function`** | The function to be optimized. |
| `MatType` | **`coordinates`** | The current function parameter. |
| `size_t` | **`epoch`** | The index of the current epoch. |
| `double` | **`objective`** | Objective value of the current point. |
If the callback returns `true`, the optimization will be terminated.
## Custom Callbacks
### StepTaken
Called after the optimizer has taken any step that modifies the coordinates.
Add this function to your callback class with your desired implementation:
```c++
template<typename OptimizerType,
typename FunctionType,
typename MatType>
bool StepTaken(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates);
```
* `optimizer`: the actual object on which `Optimize()` was called.
* `function`: the function that is being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates after the step (can be modified!).
If the callback returns `true`, the optimization will be terminated. Note that
changing the `coordinates` matrix may cause strange behavior for certain
optimizers---your mileage may vary!
### GenerationalStepTaken
Called after the evolution of a single generation, for
[multi-objective functions](#multi-objective-functions). Add this function to
your callback class with your desired implementation:
```c++
template<typename OptimizerType,
typename... FunctionTypes,
typename MatType,
typename ObjectivesVecType,
typename IndicesType>
bool GenerationalStepTaken(OptimizerType& optimizer,
std::tuple<FunctionTypes...>& functions,
MatType& coordinates,
ObjectivesVecType& objectives,
IndicesType& frontIndices);
```
* `optimizer`: the actual object on which `Optimize()` was called.
* `functions`: the functions that are being optimized (e.g. the first argument
given to `optimizer.Optimize()`.
* `coordinates`: the coordinates after taking the step.
* `objectives`: a vector of column vectors indicating the objective for each
element in the population on each objective function.
* `frontIndices`: indices of the population that are on the Pareto front.
If the callback returns `true`, the optimization will be terminated.
## Custom Callback Examples
### Learning rate scheduling
@@ -515,7 +773,7 @@ class ExponentialDecay
// Callback function called at the end of a pass over the data. We are only
// interested in the current epoch and the optimizer, we ignore the rest.
template<typename OptimizerType, typename FunctionType, typename MatType>
void EndEpoch(OptimizerType& optimizer,
bool EndEpoch(OptimizerType& optimizer,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t epoch,
@@ -524,6 +782,9 @@ class ExponentialDecay
// Update the learning rate.
optimizer.StepSize() = learningRate * (1.0 - std::pow(decay,
(double) epoch));
// Do not terminate the optimization.
return false;
}
double learningRate;
@@ -595,7 +856,7 @@ class EarlyStop
// the current objective. We are only interested in the objective and ignore
// the rest.
template<typename OptimizerType, typename FunctionType, typename MatType>
void EndEpoch(OptimizerType& /* optimizer */,
bool EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
+262 -11
View File
@@ -130,8 +130,13 @@ Each of the implemented methods is allowed to have additional cv-modifiers
The following optimizers can be used with differentiable functions:
* [L-BFGS](#l-bfgs) (`ens::L_BFGS`)
* [Forward-backward splitting (FBS)](#forward-backward-splitting-fbs) (`ens::FBS`)
* [Fast Iterative Shrinkage-Thresholding Algorithm (FISTA)](#fast-iterative-shrinkage-thresholding-algorithm-fista) (`ens::FISTA`)
* [Fast Adaptive Shrinkage/Thresholding Algorithm (FASTA)](#fast-adaptive-shrinkage-thresholding-algorithm-fasta) (`ens::FASTA`)
* [FrankWolfe](#frank-wolfe) (`ens::FrankWolfe`)
* [GradientDescent](#gradient-descent) (`ens::GradientDescent`)
* [DeltaBarDelta](#deltabardelta) (`ens::DeltaBarDelta`)
* [MomentumDeltaBarDelta](#momentum-deltabardelta) (`ens::MomentumDeltaBarDelta`)
- Any optimizer for [arbitrary functions](#arbitrary-functions)
Each of these optimizers has an `Optimize()` function that is called as
@@ -210,6 +215,12 @@ class LinearRegressionEWGFunction
g = -2 * data * v;
return arma::accu(v % v); // equivalent to \| v \|^2
}
private:
// The data.
const arma::mat& data;
// The responses to each data point.
const arma::rowvec& responses;
};
int main()
@@ -249,7 +260,7 @@ int main()
const double time1 = clock.toc();
std::cout << "LinearRegressionFunction with Evaluate() and Gradient() took "
<< time1 << " seconds to converge to the model: " << std::endl;
<< time1 << " seconds to converge to the model: " << std::endl;
std::cout << lrf1Params.t();
// Create the second objective function, which uses EvaluateWithGradient().
@@ -273,6 +284,150 @@ int main()
</details>
### Proximal operators and non-differentiable functions
Some optimization problems involve non-differentiable components, and can be
expressed as the optimization below:
$$ \operatorname{argmin}_x h(x) = \operatorname{argmin}_x f(x) + g(x). $$
Here, `f(x)` is a regular differentiable function (as in the previous
subsection), and `g(x)` is a non-differentiable arbitrary function. These
classes of functions can still be optimized using *proximal gradient
optimizers*. The following proximal gradient optimizers are implemented in
ensmallen (these can also optimize differentiable functions only, taking `g(x) =
0`):
* [Forward-backward splitting (FBS)](#forward-backward-splitting-fbs) (`ens::FBS`)
* [Fast Iterative Shrinkage-Thresholding Algorithm (FISTA)](#fast-iteartive-shrinkage-thresholding-algorithm-fista) (`ens::FISTA`)
* [Fast Adaptive Shrinkage/Thresholding Algorithm (FASTA)](#fast-adaptive-shrinkage-thresholding-algorithm-fasta) (`ens::FASTA`)
ensmallen implements a few `g(x)` options that can be used with proximal
gradient optimizers:
* `L1Penalty(`_`lambda`_`)`: $g(x) = \lambda \| x \|_1$
* `L1Constraint(`_`lambda`_`)`: $g(x)$ is the constraint $\| x \|_1 \le \lambda$
For example, by pairing `L1Penalty` with the `LinearRegressionFunction` from the
previous section, we can implement L1-penalized (sparse) linear regression:
<details>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
// Define a differentiable objective function by implementing only
// EvaluateWithGradient().
class LinearRegressionEWGFunction
{
public:
// Construct the object with the given data matrix and responses.
LinearRegressionEWGFunction(const arma::mat& dataIn,
const arma::rowvec& responsesIn) :
data(dataIn), responses(responsesIn) { }
// Simultaneously compute both the objective function and gradient for model
// parameters x. Note that this is faster than implementing Evaluate() and
// Gradient() individually because it caches the computation of
// (responses - x.t() * data)!
double EvaluateWithGradient(const arma::mat& x, arma::mat& g)
{
const arma::rowvec v = (responses - x.t() * data);
g = -2 * data * v;
return arma::accu(v % v); // equivalent to \| v \|^2
}
private:
// The data.
const arma::mat& data;
// The responses to each data point.
const arma::rowvec& responses;
};
int main()
{
// First, generate some random data, with 1000 points and 500 dimensions.
// This data has no pattern and as such will make a model that's not very
// useful---but the purpose here is just demonstration. :)
//
// For a more "real world" situation, load a dataset from file using X.load()
// and y.load() (but make sure the matrix is column-major, so that each
// observation/data point corresponds to a *column*, *not* a row.
arma::mat data(500, 1000, arma::fill::randn);
arma::rowvec responses(1000, arma::fill::randn);
// Create a starting point for our optimization as the vector of all zeros.
// The model has 500 parameters, so the shape is 500x1.
arma::mat startingPoint(500, 1, arma::fill::zeros);
// Construct the objective function f(x), and the penalty function g(x) with a
// lambda value of 0.1.
LinearRegressionEWGFunction lrf(data, responses);
ens::L1Penalty g(0.1);
// Create the FBS optimizer with default parameters, and optimize the function
// f(x) + g(x) (i.e. L1-penalized linear regression).
// The ens::FBS class can be replaced with any ensmallen proximal gradient
// optimizer.
ens::FBS fbs(g);
arma::mat lrfParams(startingPoint);
fbs.Optimize(lrf, lrfParams);
// Count the number of nonzeros in the final model.
// To get fewer nonzeros, the penalty value (0.1) could be increased.
std::cout << "Number of nonzeros in optimized parameter vector: "
<< arma::accu(lrfParams != 0) << "." << std::endl;
}
```
</details>
It is possible to implement a custom proximal operator (`g(x)`). To do so, a
class with two methods (`Evaluate()` and `BackwardStep()`) must be defined:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
// Compute the value of g(x).
double Evaluate(const arma::mat& x);
// Perform a backward step (proximal step) on `x`, given that the forward step
// size was `stepSize`.
void BackwardStep(arma::mat& x, const double stepSize);
```
</details>
A simple implementation is below for the `L1Penalty` class:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
double L1Penalty::Evaluate(const arma::mat& coordinates) const
{
// Compute the L1 penalty.
return norm(vectorise(coordinates), 1) * lambda;
}
void L1Penalty::ProximalStep(arma::mat& coordinates,
const double stepSize) const
{
// Apply the backwards step coordinate-wise.
// (See Goldstein, Studer, and Baraniuk 2009, eq. (12).)
coordinates.transform([this, stepSize](double val) { return (val > 0.0) ?
(std::max(0.0, val - lambda * stepSize)) :
(std::min(0.0, val + lambda * stepSize)); });
}
```
</details>
### Partially differentiable functions
Some differentiable functions have the additional property that the gradient
@@ -307,7 +462,7 @@ regular implementation of the `Gradient()`, so that function may be omitted.
If these functions are implemented, the following partially differentiable
function optimizers can be used:
- [Stochastic Coordinate Descent](#stochastic-coordinate-descent-scd)
- [Coordinate Descent](#coordinate-descent-cd)
## Arbitrary separable functions
@@ -358,7 +513,10 @@ Each of the implemented methods is allowed to have additional cv-modifiers
The following optimizers can be used with arbitrary separable functions:
- [CMAES](#cmaes)
- [Active CMA-ES](#active-cma-es)
- [BIPOP CMA-ES](#bipop-cma-es)
- [CMA-ES](#cma-es)
- [IPOP CMA-ES](#ipop-cma-es)
Each of these optimizers has an `Optimize()` function that is called as
`Optimize(f, x)` where `f` is the function to be optimized and `x` holds the
@@ -453,7 +611,7 @@ int main()
// parameters, so the shape is 10x1.
arma::mat params(10, 1, arma::fill::randn);
// Use the CMAES optimizer with default parameters to minimize the
// Use the CMA-ES optimizer with default parameters to minimize the
// LinearRegressionFunction.
// The ens::CMAES type can be replaced with any suitable ensmallen optimizer
// that can handle arbitrary separable functions.
@@ -461,7 +619,7 @@ int main()
LinearRegressionFunction lrf(data, responses);
cmaes.Optimize(lrf, params);
std::cout << "The optimized linear regression model found by CMAES has the "
std::cout << "The optimized linear regression model found by CMA-ES has the "
<< "parameters " << params.t();
}
```
@@ -554,9 +712,11 @@ Each of the implemented methods is allowed to have additional cv-modifiers
The following optimizers can be used with differentiable separable functions:
- [AdaBelief](#adabelief)
- [AdaBound](#adabound)
- [AdaDelta](#adadelta)
- [AdaGrad](#adagrad)
- [AdaSqrt](#adasqrt)
- [Adam](#adam)
- [AdaMax](#adamax)
- [AMSBound](#amsbound)
@@ -875,19 +1035,110 @@ arma::mat coordinates(6, 1, arma::fill::randu);
NSGA2 nsga;
double bestFrontSum = nsga.Optimize(objectives, coordinates);
// Set `bestFront` to contain all of the coordinates on the best front.
std::vector<arma::mat> bestFront = optimizer.Front();
}
// If the entire Pareto front is desired, pass it to the Optimize() function:
arma::cube front, paretoSet;
double bestFrontSum2 = nsga.Optimize(objectives, coordinates, front, paretoSet);
```
</details>
*Note*: all multi-objective function optimizers have both the function `Optimize()` to find the
best front, and also the function `Front()` to return all sets of coordinates that are on the
front.
### Performance Indicators
Performance indicators in multiobjective optimization provide essential metrics
for evaluating solution quality, such as convergence to the Pareto front and solution
diversity.
The ensmallen library offers three such indicators, aiding in the assessment and comparison
of different optimization methods:
#### Epsilon
Epsilon metric is a performance metric used in multi-objective optimization which measures
the smallest factor by which a set of solution objectives must be scaled to dominate a
reference set of solutions. Specifically, given a set of Pareto-optimal solutions, the
epsilon indicator finds the minimum value ϵ such that each solution in the set is at
least as good as every solution in the reference set when the objectives are scaled by ϵ.
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
arma::cube referenceFront(2, 1, 3);
double tol = 1e-10;
referenceFront.slice(0) = arma::vec{0.01010101, 0.89949622};
referenceFront.slice(1) = arma::vec{0.02020202, 0.85786619};
referenceFront.slice(2) = arma::vec{0.03030303, 0.82592234};
arma::cube front = referenceFront * 1.1;
// eps is approximately 1.1
double eps = Epsilon::Evaluate(front, referenceFront);
```
</details>
#### IGD
Inverse Generational Distance (IGD) is a performance metric used in multi-objective optimization
to evaluate the quality of a set of solutions relative to a reference set, typically representing
the true Pareto front. IGD measures the average distance from each point in the reference set to
the closest point in the obtained solution set.
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
arma::cube referenceFront(2, 1, 3);
double tol = 1e-10;
referenceFront.slice(0) = arma::vec{0.01010101, 0.89949622};
referenceFront.slice(1) = arma::vec{0.02020202, 0.85786619};
referenceFront.slice(2) = arma::vec{0.03030303, 0.82592234};
arma::cube front = referenceFront * 1.1;
// The third parameter is the power constant in the distance formula.
// IGD is approximately 0.05329
double igd = IGD::Evaluate(front, referenceFront, 1);
```
</details>
#### IGD Plus
IGD Plus (IGD+) is a variant of the Inverse Generational Distance (IGD) metric used in multi-objective
optimization. It refines the traditional IGD metric by incorporating a preference for Pareto-dominance
in the distance calculation. This modification helps IGD+ better reflect both the convergence to the
Pareto front and the diversity of the solution set.
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
arma::cube referenceFront(2, 1, 3);
double tol = 1e-10;
referenceFront.slice(0) = arma::vec{0.01010101, 0.89949622};
referenceFront.slice(1) = arma::vec{0.02020202, 0.85786619};
referenceFront.slice(2) = arma::vec{0.03030303, 0.82592234};
arma::cube front = referenceFront * 1.1;
// IGDPlus is approximately 0.05329
double igdPlus = IGDPlus::Evaluate(front, referenceFront);
```
</details>
*Note*: all multi-objective function optimizers have two versions of
`Optimize()`: one that finds only the best front, and one that also allows
passing `arma::cube`s (or similar) to return the Pareto set (e.g. the Pareto
optimal points in variable space) as well as the entire Pareto front (e.g. all
sets of solutions on the front):
* `Optimize(`_`functions`_`,`_`coordinates`_`)`
* `Optimize(`_`functions`_`,`_`coordinates`_`,`_`paretoSet`_`,`_`paretoFront`_`)`
The following optimizers can be used with multi-objective functions:
- [NSGA2](#nsga2)
- [MOEA/D-DE](#moead)
- [AGEMOEA](#agemoea)
#### See also:
* [Performance Assessment of Multiobjective Optimizers: An Analysis and Review](https://sop.tik.ee.ethz.ch/publicationListFiles/ztlf2003a.pdf)
* [Modified Distance Calculation in Generational Distance and Inverted Generational Distance](https://link.springer.com/chapter/10.1007/978-3-319-15892-1_8)
## Constrained functions
+1666 -245
View File
File diff suppressed because it is too large Load Diff
+49 -13
View File
@@ -15,22 +15,36 @@
#ifndef ENSMALLEN_HPP
#define ENSMALLEN_HPP
// certain compilers are way behind the curve
#if (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L))
#undef ARMA_USE_CXX11
#define ARMA_USE_CXX11
#undef ENS_HAVE_CXX14
#if (__cplusplus >= 201402L)
#define ENS_HAVE_CXX14
#endif
#if defined(_MSVC_LANG)
#if (_MSVC_LANG >= 201402L)
#undef ENS_HAVE_CXX14
#define ENS_HAVE_CXX14
#endif
#endif
#if !defined(ENS_HAVE_CXX14)
#error "*** C++14 compiler required; enable C++14 mode in your compiler, or use an earlier version of ensmallen"
#endif
#include <armadillo>
#if !defined(ARMA_USE_CXX11)
// armadillo automatically enables ARMA_USE_CXX11
// when a C++11/C++14/C++17/etc compiler is detected
#error "please enable C++11/C++14 mode in your compiler"
#if defined(COOT_VERSION_MAJOR) && \
((COOT_VERSION_MAJOR >= 2) || \
(COOT_VERSION_MAJOR == 2 && COOT_VERSION_MINOR >= 1))
// The version of Bandicoot is new enough that we can use it.
#undef ENS_HAVE_COOT
#define ENS_HAVE_COOT
#endif
#if ((ARMA_VERSION_MAJOR < 8) || ((ARMA_VERSION_MAJOR == 8) && (ARMA_VERSION_MINOR < 400)))
#error "need Armadillo version 8.400 or later"
#if ((ARMA_VERSION_MAJOR < 10) || \
((ARMA_VERSION_MAJOR == 10) && (ARMA_VERSION_MINOR < 8)))
#error "need Armadillo version 10.8 or newer"
#endif
#include <cctype>
@@ -64,7 +78,13 @@
#include "ensmallen_bits/log.hpp" // TODO: should move to another place
#include "ensmallen_bits/utility/any.hpp"
#include "ensmallen_bits/utility/arma_traits.hpp"
#include "ensmallen_bits/utility/proxies.hpp"
#include "ensmallen_bits/utility/function_traits.hpp"
#include "ensmallen_bits/utility/using.hpp"
#include "ensmallen_bits/utility/detect_callbacks.hpp"
#include "ensmallen_bits/utility/indicators/epsilon.hpp"
#include "ensmallen_bits/utility/indicators/igd.hpp"
#include "ensmallen_bits/utility/indicators/igd_plus.hpp"
// Contains traits, must be placed before report callback.
#include "ensmallen_bits/function.hpp" // TODO: should move to function/
@@ -72,27 +92,41 @@
// Callbacks.
#include "ensmallen_bits/callbacks/callbacks.hpp"
#include "ensmallen_bits/callbacks/early_stop_at_min_loss.hpp"
#include "ensmallen_bits/callbacks/grad_clip_by_norm.hpp"
#include "ensmallen_bits/callbacks/grad_clip_by_value.hpp"
#include "ensmallen_bits/callbacks/print_loss.hpp"
#include "ensmallen_bits/callbacks/progress_bar.hpp"
#include "ensmallen_bits/callbacks/query_front.hpp"
#include "ensmallen_bits/callbacks/report.hpp"
#include "ensmallen_bits/callbacks/store_best_coordinates.hpp"
#include "ensmallen_bits/callbacks/timer_stop.hpp"
#include "ensmallen_bits/problems/problems.hpp" // TODO: should move to another place
#include "ensmallen_bits/ada_belief/ada_belief.hpp"
#include "ensmallen_bits/ada_bound/ada_bound.hpp"
#include "ensmallen_bits/ada_delta/ada_delta.hpp"
#include "ensmallen_bits/ada_grad/ada_grad.hpp"
#include "ensmallen_bits/ada_sqrt/ada_sqrt.hpp"
#include "ensmallen_bits/adam/adam.hpp"
#include "ensmallen_bits/demon_adam/demon_adam.hpp"
#include "ensmallen_bits/demon_sgd/demon_sgd.hpp"
#include "ensmallen_bits/qhadam/qhadam.hpp"
#include "ensmallen_bits/aug_lagrangian/aug_lagrangian.hpp"
#include "ensmallen_bits/bigbatch_sgd/bigbatch_sgd.hpp"
#include "ensmallen_bits/cmaes/cmaes.hpp"
#include "ensmallen_bits/cmaes/active_cmaes.hpp"
#include "ensmallen_bits/cmaes/pop_cmaes.hpp"
#include "ensmallen_bits/cd/cd.hpp"
#include "ensmallen_bits/cne/cne.hpp"
#include "ensmallen_bits/de/de.hpp"
#include "ensmallen_bits/delta_bar_delta/delta_bar_delta.hpp"
#include "ensmallen_bits/delta_bar_delta/momentum_delta_bar_delta.hpp"
#include "ensmallen_bits/eve/eve.hpp"
#include "ensmallen_bits/fasta/fasta.hpp"
#include "ensmallen_bits/fbs/fbs.hpp"
#include "ensmallen_bits/fista/fista.hpp"
#include "ensmallen_bits/ftml/ftml.hpp"
#include "ensmallen_bits/fw/frank_wolfe.hpp"
#include "ensmallen_bits/gradient_descent/gradient_descent.hpp"
#include "ensmallen_bits/grid_search/grid_search.hpp"
@@ -100,6 +134,8 @@
#include "ensmallen_bits/katyusha/katyusha.hpp"
#include "ensmallen_bits/lbfgs/lbfgs.hpp"
#include "ensmallen_bits/lookahead/lookahead.hpp"
#include "ensmallen_bits/agemoea/agemoea.hpp"
#include "ensmallen_bits/moead/moead.hpp"
#include "ensmallen_bits/nsga2/nsga2.hpp"
#include "ensmallen_bits/padam/padam.hpp"
#include "ensmallen_bits/parallel_sgd/parallel_sgd.hpp"
@@ -108,7 +144,6 @@
#include "ensmallen_bits/sa/sa.hpp"
#include "ensmallen_bits/sarah/sarah.hpp"
#include "ensmallen_bits/scd/scd.hpp"
#include "ensmallen_bits/sdp/sdp.hpp"
#include "ensmallen_bits/sdp/lrsdp.hpp"
#include "ensmallen_bits/sdp/primal_dual.hpp"
@@ -125,5 +160,6 @@
#include "ensmallen_bits/svrg/svrg.hpp"
#include "ensmallen_bits/swats/swats.hpp"
#include "ensmallen_bits/wn_grad/wn_grad.hpp"
#include "ensmallen_bits/yogi/yogi.hpp"
#endif
@@ -0,0 +1,186 @@
/**
* @file ada_belief.hpp
* @author Marcus Edel
*
* Class wrapper for the AdaBelief update Policy. The intuition for AdaBelief is
* to adapt the stepsize according to the "belief" in the current gradient
* direction.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_ADA_BELIEF_HPP
#define ENSMALLEN_ADA_BELIEF_HPP
#include <ensmallen_bits/sgd/sgd.hpp>
#include "ada_belief_update.hpp"
namespace ens {
/**
* The intuition for AdaBelief is to adapt the stepsize according to the
* "belief" in the current gradient direction. For more information, see the
* following.
*
* @code
* @misc{zhuang2020adabelief,
* title = {AdaBelief Optimizer: Adapting Stepsizes by the Belief in
* Observed Gradients},
* author = {Juntang Zhuang and Tommy Tang and Sekhar Tatikonda and
* Nicha Dvornek and Yifan Ding and Xenophon Papademetris
* and James S. Duncan},
* year = {2020},
* eprint = {2010.07468},
* archivePrefix = {arXiv},
* }
* @endcode
*
* AdaBelief can optimize differentiable separable functions. For more details,
* see the documentation on function types included with this distribution or
* on the ensmallen website.
*/
class AdaBelief
{
public:
/**
* Construct the AdaBelief optimizer with the given function and parameters.
* AdaBelief is sensitive to its parameters and hence a good hyperparameter
* selection is necessary as its default may not fit every case.
*
* The maximum number of iterations refers to the maximum number of
* points that are processed (i.e., one iteration equals one point; one
* iteration does not equal one pass over the dataset).
*
* @param stepSize Step size for each iteration.
* @param batchSize Number of points to process in a single step.
* @param beta1 The exponential decay rate for the 1st moment estimates.
* @param beta2 The exponential decay rate for the 2nd moment estimates.
* @param epsilon A small constant for numerical stability.
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param shuffle If true, the function order is shuffled; otherwise, each
* function is visited in linear order.
* @param resetPolicy If true, parameters are reset before every Optimize
* call; otherwise, their values are retained.
* @param exactObjective Calculate the exact objective (Default: estimate the
* final objective obtained on the last pass over the data).
*/
AdaBelief(const double stepSize = 0.001,
const size_t batchSize = 32,
const double beta1 = 0.9,
const double beta2 = 0.999,
const double epsilon = 1e-12,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true,
const bool exactObjective = false);
/**
* Optimize the given function using AdaBelief. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam SeparableFunctionType Type of the function to optimize.
* @tparam MatType Type of matrix to optimize with.
* @tparam GradType Type of matrix to use to represent function gradients.
* @tparam CallbackTypes Types of callback functions.
* @param function Function to optimize.
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize<SeparableFunctionType, MatType, GradType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Forward the MatType as GradType.
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<SeparableFunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the step size.
double StepSize() const { return optimizer.StepSize(); }
//! Modify the step size.
double& StepSize() { return optimizer.StepSize(); }
//! Get the batch size.
size_t BatchSize() const { return optimizer.BatchSize(); }
//! Modify the batch size.
size_t& BatchSize() { return optimizer.BatchSize(); }
//! Get the exponential decay rate for the 1st moment estimates.
double Beta1() const { return optimizer.UpdatePolicy().Beta1(); }
//! Modify the exponential decay rate for the 1st moment estimates.
double& Beta1() { return optimizer.UpdatePolicy().Beta1(); }
//! Get the exponential decay rate for the 2nd moment estimates.
double Beta2() const { return optimizer.UpdatePolicy().Beta2(); }
//! Get the second moment coefficient.
double& Beta2() { return optimizer.UpdatePolicy().Beta2(); }
//! Get the value for numerical stability.
double Epsilon() const { return optimizer.UpdatePolicy().Epsilon(); }
//! Modify the value used for numerical stability.
double& Epsilon() { return optimizer.UpdatePolicy().Epsilon(); }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return optimizer.MaxIterations(); }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return optimizer.MaxIterations(); }
//! Get the tolerance for termination.
double Tolerance() const { return optimizer.Tolerance(); }
//! Modify the tolerance for termination.
double& Tolerance() { return optimizer.Tolerance(); }
//! Get whether or not the individual functions are shuffled.
bool Shuffle() const { return optimizer.Shuffle(); }
//! Modify whether or not the individual functions are shuffled.
bool& Shuffle() { return optimizer.Shuffle(); }
//! Get whether or not the actual objective is calculated.
bool ExactObjective() const { return optimizer.ExactObjective(); }
//! Modify whether or not the actual objective is calculated.
bool& ExactObjective() { return optimizer.ExactObjective(); }
//! Get whether or not the update policy parameters are reset before
//! Optimize call.
bool ResetPolicy() const { return optimizer.ResetPolicy(); }
//! Modify whether or not the update policy parameters
//! are reset before Optimize call.
bool& ResetPolicy() { return optimizer.ResetPolicy(); }
private:
//! The Stochastic Gradient Descent object with AdaBelief policy.
SGD<AdaBeliefUpdate> optimizer;
};
} // namespace ens
// Include implementation.
#include "ada_belief_impl.hpp"
#endif
@@ -0,0 +1,44 @@
/**
* @file ada_belief_impl.hpp
* @author Marcus Edel
*
* Implementation of AdaBelief class wrapper.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_ADA_BELIEF_ADA_BELIEF_IMPL_HPP
#define ENSMALLEN_ADA_BELIEF_ADA_BELIEF_IMPL_HPP
// In case it hasn't been included yet.
#include "ada_belief.hpp"
namespace ens {
inline AdaBelief::AdaBelief(
const double stepSize,
const size_t batchSize,
const double beta1,
const double beta2,
const double epsilon,
const size_t maxIterations,
const double tolerance,
const bool shuffle,
const bool resetPolicy,
const bool exactObjective) :
optimizer(stepSize,
batchSize,
maxIterations,
tolerance,
shuffle,
AdaBeliefUpdate(epsilon, beta1, beta2),
NoDecay(),
resetPolicy,
exactObjective)
{ /* Nothing to do. */ }
} // namespace ens
#endif
@@ -0,0 +1,166 @@
/**
* @file ada_belief_update.hpp
* @author Marcus Edel
*
* AdaBelief optimizer update policy. The intuition for AdaBelief is to adapt
* the stepsize according to the "belief" in the current gradient direction.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_ADA_BELIEF_ADA_BELIEF_UPDATE_HPP
#define ENSMALLEN_ADA_BELIEF_ADA_BELIEF_UPDATE_HPP
namespace ens {
/**
* The intuition for AdaBelief is to adapt the stepsize according to the
* "belief" in the current gradient direction.
*
* For more information, see the following.
*
* @code
* @misc{zhuang2020adabelief,
* title = {AdaBelief Optimizer: Adapting Stepsizes by the Belief in
* Observed Gradients},
* author = {Juntang Zhuang and Tommy Tang and Sekhar Tatikonda and
* Nicha Dvornek and Yifan Ding and Xenophon Papademetris
* and James S. Duncan},
* year = {2020},
* eprint = {2010.07468},
* archivePrefix = {arXiv},
* }
* @endcode
*/
class AdaBeliefUpdate
{
public:
/**
* Construct the AdaBelief update policy with the given parameters.
*
* @param epsilon A small constant for numerical stability.
* @param beta1 The exponential decay rate for the 1st moment estimates.
* @param beta2 The exponential decay rate for the 2nd moment estimates.
*/
AdaBeliefUpdate(const double epsilon = 1e-8,
const double beta1 = 0.9,
const double beta2 = 0.999) :
epsilon(epsilon),
beta1(beta1),
beta2(beta2)
{
// Nothing to do.
}
//! Get the value for numerical stability.
double Epsilon() const { return epsilon; }
//! Modify the value used for numerical stability.
double& Epsilon() { return epsilon; }
//! Get the exponential decay rate for the 1st moment estimates.
double Beta1() const { return beta1; }
//! Modify the exponential decay rate for the 1st moment estimates.
double& Beta1() { return beta1; }
//! Get the exponential decay rate for the 2nd moment estimates.
double Beta2() const { return beta2; }
//! Modify the exponential decay rate for the 2nd moment estimates.
double& Beta2() { return beta2; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent AdaBeliefUpdate object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdaBeliefUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
epsilon(ElemType(parent.epsilon)),
iteration(0)
{
m.zeros(rows, cols);
s.zeros(rows, cols);
// Prevent underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
* Update step for AdaBelief.
*
* @param iterate Parameters that minimize the function.
* @param stepSize Step size to be used for the given iteration.
* @param gradient The gradient matrix.
*/
void Update(MatType& iterate,
const double stepSize,
const GradType& gradient)
{
// Increment the iteration counter variable.
++iteration;
m *= beta1;
m += (1 - beta1) * gradient;
s *= beta2;
s += (1 - beta2) * pow(gradient - m, 2) + epsilon;
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
// And update the iterate.
iterate -= ((m / biasCorrection1) * ElemType(stepSize)) /
(sqrt(s / biasCorrection2) + epsilon);
}
private:
//! Instantiated parent object.
AdaBeliefUpdate& parent;
//! The exponential moving average of gradient values.
GradType m;
// The exponential moving average of squared gradient values.
GradType s;
// Parent parameters converted to the element type of the matrix.
ElemType beta1;
ElemType beta2;
ElemType epsilon;
// The number of iterations.
size_t iteration;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
// The xponential decay rate for the 1st moment estimates.
double beta1;
// The exponential decay rate for the 2nd moment estimates.
double beta2;
};
} // namespace ens
#endif
@@ -107,7 +107,7 @@ class AdaBoundType
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(DecomposableFunctionType& function,
MatType& iterate,
@@ -56,8 +56,7 @@ class AdaBoundUpdate
gamma(gamma),
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
beta2(beta2)
{
// Nothing to do.
}
@@ -87,11 +86,6 @@ class AdaBoundUpdate
//! Modify the second moment coefficient.
double& Beta2() { return beta2; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -102,6 +96,8 @@ class AdaBoundUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
@@ -111,10 +107,24 @@ class AdaBoundUpdate
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdaBoundUpdate& parent, const size_t rows, const size_t cols) :
parent(parent), first(true), initialStepSize(0)
parent(parent),
finalLr(ElemType(parent.finalLr)),
gamma(ElemType(parent.gamma)),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
first(true),
initialStepSize(0),
iteration(0)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
// Check for underflows in conversions.
if (gamma == ElemType(0) && parent.gamma != 0.0)
gamma = 10 * std::numeric_limits<ElemType>::epsilon();
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -135,34 +145,30 @@ class AdaBoundUpdate
if (first)
{
first = false;
initialStepSize = stepSize;
initialStepSize = ElemType(stepSize);
}
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// Decay the first and second moment running average coefficient.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * (gradient % gradient);
v *= beta2;
v += (1 - beta2) * (gradient % gradient);
const ElemType biasCorrection1 = 1.0 - std::pow(parent.beta1,
parent.iteration);
const ElemType biasCorrection2 = 1.0 - std::pow(parent.beta2,
parent.iteration);
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
const ElemType fl = parent.finalLr * stepSize / initialStepSize;
const ElemType lower = fl * (1.0 - 1.0 / (parent.gamma *
parent.iteration + 1));
const ElemType upper = fl * (1.0 + 1.0 / (parent.gamma *
parent.iteration));
const ElemType fl = finalLr * ElemType(stepSize) / initialStepSize;
const ElemType lower = fl * (1 - 1 / (gamma * iteration + 1));
const ElemType upper = fl * (1 + 1 / (gamma * iteration));
// Applies bounds on actual learning rate.
iterate -= arma::clamp((stepSize *
std::sqrt(biasCorrection2) / biasCorrection1) / (arma::sqrt(v) +
parent.epsilon), lower, upper) % m;
// Applies bounds on actual learning rate.
iterate -= clamp((ElemType(stepSize) *
std::sqrt(biasCorrection2) / biasCorrection1) / (sqrt(v) + epsilon),
lower, upper) % m;
}
private:
@@ -175,11 +181,21 @@ class AdaBoundUpdate
// The exponential moving average of squared gradient values.
GradType v;
// Parameters of the parent, casted to the element type of the problem.
ElemType finalLr;
ElemType gamma;
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// Whether this is the first call of the Update method.
bool first;
// The initial (Adam) learning rate.
double initialStepSize;
ElemType initialStepSize;
// The number of iterations.
size_t iteration;
};
private:
@@ -197,9 +213,6 @@ class AdaBoundUpdate
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
@@ -56,8 +56,7 @@ class AMSBoundUpdate
gamma(gamma),
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
beta2(beta2)
{
// Nothing to do.
}
@@ -87,11 +86,6 @@ class AMSBoundUpdate
//! Modify the second moment coefficient.
double& Beta2() { return beta2; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -102,6 +96,8 @@ class AMSBoundUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
@@ -111,11 +107,25 @@ class AMSBoundUpdate
* @param cols Number of columns in the gradient matrix.
*/
Policy(AMSBoundUpdate& parent, const size_t rows, const size_t cols) :
parent(parent), first(true), initialStepSize(0)
parent(parent),
finalLr(ElemType(parent.finalLr)),
gamma(ElemType(parent.gamma)),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
first(true),
initialStepSize(0),
iteration(0)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
vImproved.zeros(rows, cols);
// Check for underflows in conversions.
if (gamma == ElemType(0) && parent.gamma != 0.0)
gamma = 10 * std::numeric_limits<ElemType>::epsilon();
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -129,44 +139,36 @@ class AMSBoundUpdate
const double stepSize,
const GradType& gradient)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
// Save the initial step size.
if (first)
{
first = false;
initialStepSize = stepSize;
initialStepSize = ElemType(stepSize);
}
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// Decay the first and second moment running average coefficient.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * (gradient % gradient);
v *= beta2;
v += (1 - beta2) * (gradient % gradient);
const ElemType biasCorrection1 = 1.0 - std::pow(parent.beta1,
parent.iteration);
const ElemType biasCorrection2 = 1.0 - std::pow(parent.beta2,
parent.iteration);
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
const ElemType fl = parent.finalLr * stepSize / initialStepSize;
const ElemType lower = fl * (1.0 - 1.0 / (parent.gamma *
parent.iteration + 1));
const ElemType upper = fl * (1.0 + 1.0 / (parent.gamma *
parent.iteration));
const ElemType fl = finalLr * ElemType(stepSize) / initialStepSize;
const ElemType lower = fl * (1 - 1 / (gamma * iteration + 1));
const ElemType upper = fl * (1 + 1 / (gamma * iteration));
// Element wise maximum of past and present squared gradients.
vImproved = arma::max(vImproved, v);
vImproved = max(vImproved, v);
// Applies bounds on actual learning rate.
iterate -= arma::clamp((stepSize *
std::sqrt(biasCorrection2) / biasCorrection1) /
(arma::sqrt(vImproved) + parent.epsilon), lower, upper) % m;
iterate -= clamp((ElemType(stepSize) * std::sqrt(biasCorrection2) /
biasCorrection1) / (sqrt(vImproved) + epsilon), lower, upper) % m;
}
private:
@@ -179,14 +181,24 @@ class AMSBoundUpdate
// The exponential moving average of squared gradient values.
GradType v;
// Parameters of the parent, casted to the element type of the problem.
ElemType finalLr;
ElemType gamma;
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// Whether this is the first call of the Update method.
bool first;
// The initial (Adam) learning rate.
double initialStepSize;
ElemType initialStepSize;
// The optimal squared gradient value.
GradType vImproved;
// The number of iterations.
size_t iteration;
};
private:
@@ -204,9 +216,6 @@ class AMSBoundUpdate
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
@@ -98,7 +98,7 @@ class AdaDelta
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
@@ -71,6 +71,8 @@ class AdaDeltaUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD optimizer method before the start
* of the iteration update process. In AdaDelta update policy, the mean
@@ -82,10 +84,16 @@ class AdaDeltaUpdate
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdaDeltaUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
parent(parent),
rho(ElemType(parent.rho)),
epsilon(ElemType(parent.epsilon))
{
meanSquaredGradient.zeros(rows, cols);
meanSquaredGradientDx.zeros(rows, cols);
// Check for underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -102,17 +110,17 @@ class AdaDeltaUpdate
const GradType& gradient)
{
// Accumulate gradient.
meanSquaredGradient *= parent.rho;
meanSquaredGradient += (1 - parent.rho) * (gradient % gradient);
GradType dx = arma::sqrt((meanSquaredGradientDx + parent.epsilon) /
(meanSquaredGradient + parent.epsilon)) % gradient;
meanSquaredGradient *= rho;
meanSquaredGradient += (1 - rho) * (gradient % gradient);
GradType dx = sqrt((meanSquaredGradientDx + epsilon) /
(meanSquaredGradient + epsilon)) % gradient;
// Accumulate updates.
meanSquaredGradientDx *= parent.rho;
meanSquaredGradientDx += (1 - parent.rho) * (dx % dx);
meanSquaredGradientDx *= rho;
meanSquaredGradientDx += (1 - rho) * (dx % dx);
// Apply update.
iterate -= (stepSize * dx);
iterate -= (ElemType(stepSize) * dx);
}
private:
@@ -124,6 +132,10 @@ class AdaDeltaUpdate
// The delta mean squared gradient matrix.
GradType meanSquaredGradientDx;
// Parameters of the update, converted to the matrix element type.
ElemType rho;
ElemType epsilon;
};
private:
+1 -1
View File
@@ -94,7 +94,7 @@ class AdaGrad
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
@@ -64,6 +64,8 @@ class AdaGradUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD optimizer before the start of the
* iteration update process. In AdaGrad update policy, squared gradient
@@ -76,10 +78,14 @@ class AdaGradUpdate
*/
Policy(AdaGradUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
squaredGradient(rows, cols)
squaredGradient(rows, cols),
epsilon(ElemType(parent.epsilon))
{
// Initialize an empty matrix for sum of squares of parameter gradient.
squaredGradient.zeros();
// Detect underflow for epsilon and try to address it.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -96,8 +102,8 @@ class AdaGradUpdate
const GradType& gradient)
{
squaredGradient += (gradient % gradient);
iterate -= (stepSize * gradient) / (arma::sqrt(squaredGradient) +
parent.epsilon);
iterate -= (ElemType(stepSize) * gradient) / (sqrt(squaredGradient) +
epsilon);
}
private:
@@ -105,6 +111,8 @@ class AdaGradUpdate
AdaGradUpdate& parent;
// The squared gradient matrix.
GradType squaredGradient;
// The epsilon value, converted to the element type of the matrix.
ElemType epsilon;
};
private:
@@ -0,0 +1,168 @@
/**
* @file ada_sqrt.hpp
* @author Marcus Edel
*
* Implementation of the AdaSqrt optimizer. AdaSqrt is an optimizer that
* chooses learning rate dynamically by adapting to the data and iteration.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_ADA_SQRT_ADA_SQRT_HPP
#define ENSMALLEN_ADA_SQRT_ADA_SQRT_HPP
#include "../sgd/sgd.hpp"
#include "ada_sqrt_update.hpp"
namespace ens {
/**
* AdaSqrt is a modified version of stochastic gradient descent which performs
* larger updates for more sparse parameters and smaller updates for less sparse
* parameters.
*
* For more information, see the following.
*
* @code
* @misc{hu2019secondorder,
* title = {Second-order Information in First-order Optimization Methods},
* author = {Yuzheng Hu and Licong Lin and Shange Tang},
* year = {2019},
* eprint = {1912.09926},
* }
* @endcode
*
* AdaSqrt can optimize differentiable separable functions. For more details,
* see the documentation on function types included with this distribution or on
* the ensmallen website.
*/
class AdaSqrt
{
public:
/**
* Construct the AdaSqrt optimizer with the given function and parameters.
* The defaults here are not necessarily good for the given problem, so it is
* suggested that the values used be tailored to the task at hand. The
* maximum number of iterations refers to the maximum number of points that
* are processed (i.e., one iteration equals one point; one iteration does not
* equal one pass over the dataset).
*
* @param stepSize Step size for each iteration.
* @param batchSize Number of points to process in one step.
* @param epsilon Value used to initialise the squared gradient parameter.
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param shuffle If true, the function order is shuffled; otherwise, each
* function is visited in linear order.
* @param resetPolicy If true, parameters are reset before every Optimize
* call; otherwise, their values are retained.
* @param exactObjective Calculate the exact objective (Default: estimate the
* final objective obtained on the last pass over the data).
*/
AdaSqrt(const double stepSize = 0.01,
const size_t batchSize = 32,
const double epsilon = 1e-8,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true,
const bool exactObjective = false);
/**
* Optimize the given function using AdaSqrt. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam SeparableFunctionType Type of the function to be optimized.
* @tparam MatType Type of matrix to optimize with.
* @tparam GradType Type of matrix to use to represent function gradients.
* @tparam CallbackTypes Types of callback functions.
* @param function Function to optimize.
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize<SeparableFunctionType, MatType, GradType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Forward the MatType as GradType.
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<SeparableFunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the step size.
double StepSize() const { return optimizer.StepSize(); }
//! Modify the step size.
double& StepSize() { return optimizer.StepSize(); }
//! Get the batch size.
size_t BatchSize() const { return optimizer.BatchSize(); }
//! Modify the batch size.
size_t& BatchSize() { return optimizer.BatchSize(); }
//! Get the value used to initialise the squared gradient parameter.
double Epsilon() const { return optimizer.UpdatePolicy().Epsilon(); }
//! Modify the value used to initialise the squared gradient parameter.
double& Epsilon() { return optimizer.UpdatePolicy().Epsilon(); }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return optimizer.MaxIterations(); }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return optimizer.MaxIterations(); }
//! Get the tolerance for termination.
double Tolerance() const { return optimizer.Tolerance(); }
//! Modify the tolerance for termination.
double& Tolerance() { return optimizer.Tolerance(); }
//! Get whether or not the individual functions are shuffled.
bool Shuffle() const { return optimizer.Shuffle(); }
//! Modify whether or not the individual functions are shuffled.
bool& Shuffle() { return optimizer.Shuffle(); }
//! Get whether or not the actual objective is calculated.
bool ExactObjective() const { return optimizer.ExactObjective(); }
//! Modify whether or not the actual objective is calculated.
bool& ExactObjective() { return optimizer.ExactObjective(); }
//! Get whether or not the update policy parameters
//! are reset before Optimize call.
bool ResetPolicy() const { return optimizer.ResetPolicy(); }
//! Modify whether or not the update policy parameters
//! are reset before Optimize call.
bool& ResetPolicy() { return optimizer.ResetPolicy(); }
private:
//! The Stochastic Gradient Descent object with AdaSqrt policy.
SGD<AdaSqrtUpdate> optimizer;
};
} // namespace ens
// Include implementation.
#include "ada_sqrt_impl.hpp"
#endif
@@ -0,0 +1,38 @@
/**
* @file ada_sqrt_impl.hpp
* @author Marcus Edel
*
* Implementation of AdaSqrt optimizer.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_ADA_SQRT_ADA_SQRT_IMPL_HPP
#define ENSMALLEN_ADA_SQRT_ADA_SQRT_IMPL_HPP
namespace ens {
inline AdaSqrt::AdaSqrt(const double stepSize,
const size_t batchSize,
const double epsilon,
const size_t maxIterations,
const double tolerance,
const bool shuffle,
const bool resetPolicy,
const bool exactObjective) :
optimizer(stepSize,
batchSize,
maxIterations,
tolerance,
shuffle,
AdaSqrtUpdate(epsilon),
NoDecay(),
resetPolicy,
exactObjective)
{ /* Nothing to do. */ }
} // namespace ens
#endif
@@ -0,0 +1,126 @@
/**
* @file ada_sqrt_update.hpp
* @author Marcus Edel
*
* AdaSqrt update for Stochastic Gradient Descent.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_ADA_SQRT_ADA_SQRT_UPDATE_HPP
#define ENSMALLEN_ADA_SQRT_ADA_SQRT_UPDATE_HPP
namespace ens {
/**
* Implementation of the AdaSqrt update policy. AdaSqrt update policy chooses
* learning rate dynamically by adapting to the data and iteration.
*
* For more information, see the following.
*
* @code
* @misc{hu2019secondorder,
* title = {Second-order Information in First-order Optimization Methods},
* author = {Yuzheng Hu and Licong Lin and Shange Tang},
* year = {2019},
* eprint = {1912.09926},
* }
* @endcode
*
*/
class AdaSqrtUpdate
{
public:
/**
* Construct the AdaSqrt update policy with given epsilon parameter.
*
* @param epsilon The epsilon value used to initialise the squared gradient
* parameter.
*/
AdaSqrtUpdate(const double epsilon = 1e-8) : epsilon(epsilon)
{
// Nothing to do.
}
//! Get the value used to initialise the squared gradient parameter.
double Epsilon() const { return epsilon; }
//! Modify the value used to initialise the squared gradient parameter.
double& Epsilon() { return epsilon; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD optimizer before the start of the
* iteration update process. In AdaSqrt update policy, squared gradient
* matrix is initialized to the zeros matrix with the same size as gradient
* matrix (see ens::SGD<>).
*
* @param parent Instantiated parent class.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdaSqrtUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
squaredGradient(rows, cols),
epsilon(ElemType(parent.epsilon)),
iteration(0)
{
// Initialize an empty matrix for sum of squares of parameter gradient.
squaredGradient.zeros();
// Check for underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
* Update step for SGD. The AdaSqrt update adapts the learning rate by
* performing larger updates for more sparse parameters and smaller updates
* for less sparse parameters.
*
* @param iterate Parameters that minimize the function.
* @param stepSize Step size to be used for the given iteration.
* @param gradient The gradient matrix.
*/
void Update(MatType& iterate,
const double stepSize,
const GradType& gradient)
{
++iteration;
squaredGradient += square(gradient);
iterate -= ElemType(stepSize) * std::sqrt(ElemType(iteration)) *
gradient / (squaredGradient + epsilon);
}
private:
// Instantiated parent class.
AdaSqrtUpdate& parent;
// The squared gradient matrix.
GradType squaredGradient;
// Epsilon converted to the element type of the optimization.
ElemType epsilon;
// The number of iterations.
size_t iteration;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
};
} // namespace ens
#endif
+1 -1
View File
@@ -120,7 +120,7 @@ class AdamType
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
+30 -22
View File
@@ -52,8 +52,7 @@ class AdamUpdate
const double beta2 = 0.999) :
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
beta2(beta2)
{
// Nothing to do.
}
@@ -73,11 +72,6 @@ class AdamUpdate
//! Modify the second moment coefficient.
double& Beta2() { return beta2; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -88,6 +82,8 @@ class AdamUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
@@ -97,10 +93,18 @@ class AdamUpdate
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdamUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
parent(parent),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
iteration(0)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
// Attempt to detect underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -115,27 +119,26 @@ class AdamUpdate
const GradType& gradient)
{
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * (gradient % gradient);
v *= beta2;
v += (1 - beta2) * square(gradient);
const double biasCorrection1 = 1.0 - std::pow(parent.beta1,
parent.iteration);
const double biasCorrection2 = 1.0 - std::pow(parent.beta2,
parent.iteration);
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
/**
* It should be noted that the term, m / (arma::sqrt(v) + eps), in the
* following expression is an approximation of the following actual term;
* m / (arma::sqrt(v) + (arma::sqrt(biasCorrection2) * eps).
*/
iterate -= (stepSize * std::sqrt(biasCorrection2) / biasCorrection1) *
m / (arma::sqrt(v) + parent.epsilon);
iterate -= (ElemType(stepSize) *
std::sqrt(biasCorrection2) / biasCorrection1) *
m / (sqrt(v) + epsilon);
}
private:
@@ -147,6 +150,14 @@ class AdamUpdate
// The exponential moving average of squared gradient values.
GradType v;
// Parameters converted to the element type of the optimization.
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// The number of iterations.
size_t iteration;
};
private:
@@ -158,9 +169,6 @@ class AdamUpdate
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
+29 -24
View File
@@ -30,11 +30,11 @@ namespace ens {
*
* @code
* @article{Kingma2014,
* author = {Diederik P. Kingma and Jimmy Ba},
* title = {Adam: {A} Method for Stochastic Optimization},
* journal = {CoRR},
* year = {2014},
* url = {http://arxiv.org/abs/1412.6980}
* author = {Diederik P. Kingma and Jimmy Ba},
* title = {Adam: {A} Method for Stochastic Optimization},
* journal = {CoRR},
* year = {2014},
* url = {http://arxiv.org/abs/1412.6980}
* }
* @endcode
*/
@@ -54,8 +54,7 @@ class AdaMaxUpdate
const double beta2 = 0.999) :
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
beta2(beta2)
{
// Nothing to do.
}
@@ -75,11 +74,6 @@ class AdaMaxUpdate
//! Modify the second moment coefficient.
double& Beta2() { return beta2; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -90,6 +84,8 @@ class AdaMaxUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
@@ -99,10 +95,17 @@ class AdaMaxUpdate
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdaMaxUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
parent(parent),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
iteration(0)
{
m.zeros(rows, cols);
u.zeros(rows, cols);
// Attempt to detect underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -117,21 +120,20 @@ class AdaMaxUpdate
const GradType& gradient)
{
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
// Update the exponentially weighted infinity norm.
u *= parent.beta2;
u = arma::max(u, arma::abs(gradient));
u *= beta2;
u = max(u, abs(gradient));
const double biasCorrection1 = 1.0 - std::pow(parent.beta1,
parent.iteration);
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
if (biasCorrection1 != 0)
iterate -= (stepSize / biasCorrection1 * m / (u + parent.epsilon));
iterate -= (ElemType(stepSize) / biasCorrection1 * m / (u + epsilon));
}
private:
@@ -141,6 +143,12 @@ class AdaMaxUpdate
GradType m;
// The exponentially weighted infinity norm.
GradType u;
// Tuning parameters converted to the element type of the optimization.
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// The number of iterations.
size_t iteration;
};
private:
@@ -152,9 +160,6 @@ class AdaMaxUpdate
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
+35 -27
View File
@@ -2,7 +2,7 @@
* @file amsgrad_update.hpp
* @author Haritha Nair
*
* Implementation of AMSGrad optimizer. AMSGrad is an exponential moving average
* Implementation of AMSGrad optimizer. AMSGrad is an exponential moving average
* optimizer that dynamically adapts over time with guaranteed convergence.
*
* ensmallen is free software; you may redistribute it and/or modify it under
@@ -25,9 +25,9 @@ namespace ens {
*
* @code
* @article{
* title = {On the convergence of Adam and beyond},
* url = {https://openreview.net/pdf?id=ryQu7f-RZ}
* year = {2018}
* title = {On the convergence of Adam and beyond},
* url = {https://openreview.net/pdf?id=ryQu7f-RZ}
* year = {2018}
* }
* @endcode
*/
@@ -47,8 +47,7 @@ class AMSGradUpdate
const double beta2 = 0.999) :
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
beta2(beta2)
{
// Nothing to do.
}
@@ -68,11 +67,6 @@ class AMSGradUpdate
//! Modify the second moment coefficient.
double& Beta2() { return beta2; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -83,6 +77,8 @@ class AMSGradUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
@@ -92,11 +88,19 @@ class AMSGradUpdate
* @param cols Number of columns in the gradient matrix.
*/
Policy(AMSGradUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
parent(parent),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
iteration(0)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
vImproved.zeros(rows, cols);
// Attempt to detect underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -111,25 +115,24 @@ class AMSGradUpdate
const GradType& gradient)
{
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * (gradient % gradient);
v *= beta2;
v += (1 - beta2) * (gradient % gradient);
const double biasCorrection1 = 1.0 - std::pow(parent.beta1,
parent.iteration);
const double biasCorrection2 = 1.0 - std::pow(parent.beta2,
parent.iteration);
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
// Element wise maximum of past and present squared gradients.
vImproved = arma::max(vImproved, v);
vImproved = max(vImproved, v);
iterate -= (stepSize * std::sqrt(biasCorrection2) / biasCorrection1) *
m / (arma::sqrt(vImproved) + parent.epsilon);
iterate -= (ElemType(stepSize) *
std::sqrt(biasCorrection2) / biasCorrection1) *
m / (sqrt(vImproved) + epsilon);
}
private:
@@ -144,6 +147,14 @@ class AMSGradUpdate
// The optimal squared gradient value.
GradType vImproved;
// Parameters converted to the element type of the optimization.
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// The number of iterations.
size_t iteration;
};
private:
@@ -155,9 +166,6 @@ class AMSGradUpdate
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
+37 -30
View File
@@ -50,8 +50,7 @@ class NadamUpdate
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
scheduleDecay(scheduleDecay),
iteration(0)
scheduleDecay(scheduleDecay)
{
// Nothing to do.
}
@@ -76,11 +75,6 @@ class NadamUpdate
//! Modify the decay parameter for decay coefficients
double& ScheduleDecay() { return scheduleDecay; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -91,6 +85,8 @@ class NadamUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the optimizer before the start of the
* iteration update process.
@@ -101,10 +97,18 @@ class NadamUpdate
*/
Policy(NadamUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
cumBeta1(1)
cumBeta1(1),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
iteration(0)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
// Attempt to detect underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -119,36 +123,34 @@ class NadamUpdate
const GradType& gradient)
{
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * gradient % gradient;
v *= beta2;
v += (1 - beta2) * gradient % gradient;
double beta1T = parent.beta1 * (1 - (0.5 *
std::pow(0.96, parent.iteration * parent.scheduleDecay)));
ElemType beta1T = beta1 * (1 - ElemType(0.5 *
std::pow(0.96, iteration * parent.scheduleDecay)));
double beta1T1 = parent.beta1 * (1 - (0.5 *
std::pow(0.96, (parent.iteration + 1) * parent.scheduleDecay)));
ElemType beta1T1 = beta1 * (1 - ElemType(0.5 *
std::pow(0.96, (iteration + 1) * parent.scheduleDecay)));
cumBeta1 *= beta1T;
const double biasCorrection1 = 1.0 - cumBeta1;
const double biasCorrection2 = 1.0 - std::pow(parent.beta2,
parent.iteration);
const double biasCorrection3 = 1.0 - (cumBeta1 * beta1T1);
const ElemType biasCorrection1 = 1 - cumBeta1;
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
const ElemType biasCorrection3 = 1 - (cumBeta1 * beta1T1);
/* Note :- arma::sqrt(v) + epsilon * sqrt(biasCorrection2) is approximated
* as arma::sqrt(v) + epsilon
*/
iterate -= (stepSize * (((1 - beta1T) / biasCorrection1) * gradient
+ (beta1T1 / biasCorrection3) * m) * sqrt(biasCorrection2))
/ (arma::sqrt(v) + parent.epsilon);
iterate -= (ElemType(stepSize) *
(((1 - beta1T) / biasCorrection1) * gradient +
(beta1T1 / biasCorrection3) * m) * std::sqrt(biasCorrection2)) /
(sqrt(v) + epsilon);
}
private:
@@ -162,7 +164,15 @@ class NadamUpdate
GradType v;
// The cumulative product of decay coefficients.
double cumBeta1;
ElemType cumBeta1;
// Parameters converted to the element type of the optimization.
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// The number of iterations.
size_t iteration;
};
private:
@@ -177,9 +187,6 @@ class NadamUpdate
// The decay parameter for decay coefficients.
double scheduleDecay;
// The number of iterations.
size_t iteration;
};
} // namespace ens
+34 -25
View File
@@ -50,8 +50,7 @@ class NadaMaxUpdate
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
scheduleDecay(scheduleDecay),
iteration(0)
scheduleDecay(scheduleDecay)
{
// Nothing to do.
}
@@ -76,11 +75,6 @@ class NadaMaxUpdate
//! Modify the decay parameter for decay coefficients
double& ScheduleDecay() { return scheduleDecay; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -91,6 +85,8 @@ class NadaMaxUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor method is called by the optimizer before the start of
* the iteration update process.
@@ -101,10 +97,18 @@ class NadaMaxUpdate
*/
Policy(NadaMaxUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
cumBeta1(1)
cumBeta1(1),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
iteration(0)
{
m.zeros(rows, cols);
u.zeros(rows, cols);
// Attempt to detect underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -119,30 +123,30 @@ class NadaMaxUpdate
const GradType& gradient)
{
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
u = arma::max(u * parent.beta2, arma::abs(gradient));
u = max(u * beta2, abs(gradient));
double beta1T = parent.beta1 * (1 - (0.5 *
std::pow(0.96, parent.iteration * parent.scheduleDecay)));
ElemType beta1T = beta1 * (1 - ElemType(0.5 *
std::pow(0.96, iteration * parent.scheduleDecay)));
double beta1T1 = parent.beta1 * (1 - (0.5 *
std::pow(0.96, (parent.iteration + 1) * parent.scheduleDecay)));
ElemType beta1T1 = beta1 * (1 - ElemType(0.5 *
std::pow(0.96, (iteration + 1) * parent.scheduleDecay)));
cumBeta1 *= beta1T;
const double biasCorrection1 = 1.0 - cumBeta1;
const double biasCorrection2 = 1.0 - (cumBeta1 * beta1T1);
const ElemType biasCorrection1 = 1 - cumBeta1;
const ElemType biasCorrection2 = 1 - (cumBeta1 * beta1T1);
if ((biasCorrection1 != 0) && (biasCorrection2 != 0))
{
iterate -= (stepSize * (((1 - beta1T) / biasCorrection1) * gradient
+ (beta1T1 / biasCorrection2) * m)) / (u + parent.epsilon);
iterate -= (ElemType(stepSize) *
(((1 - beta1T) / biasCorrection1) * gradient +
(beta1T1 / biasCorrection2) * m)) / (u + epsilon);
}
}
@@ -157,7 +161,15 @@ class NadaMaxUpdate
GradType u;
// The cumulative product of decay coefficients.
double cumBeta1;
ElemType cumBeta1;
// Parameters converted to the element type of the optimization.
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// The number of iterations.
size_t iteration;
};
private:
@@ -172,9 +184,6 @@ class NadaMaxUpdate
// The decay parameter for decay coefficients.
double scheduleDecay;
// The number of iterations.
size_t iteration;
};
} // namespace ens
@@ -27,11 +27,11 @@ namespace ens {
*
* @code
* @article{
* author = {Constantinos Daskalakis, Andrew Ilyas, Vasilis Syrgkanis,
* Haoyang Zeng},
* title = {Training GANs with Optimism},
* year = {2017},
* url = {https://arxiv.org/abs/1711.00141}
* author = {Constantinos Daskalakis, Andrew Ilyas, Vasilis Syrgkanis,
* Haoyang Zeng},
* title = {Training GANs with Optimism},
* year = {2017},
* url = {https://arxiv.org/abs/1711.00141}
* }
* @endcode
*/
@@ -51,8 +51,7 @@ class OptimisticAdamUpdate
const double beta2 = 0.999) :
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
beta2(beta2)
{
// Nothing to do.
}
@@ -72,11 +71,6 @@ class OptimisticAdamUpdate
//! Modify the second moment coefficient.
double& Beta2() { return beta2; }
//! Get the current iteration number.
size_t Iteration() const { return iteration; }
//! Modify the current iteration number.
size_t& Iteration() { return iteration; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
@@ -87,6 +81,8 @@ class OptimisticAdamUpdate
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
@@ -96,11 +92,19 @@ class OptimisticAdamUpdate
* @param cols Number of columns in the gradient matrix.
*/
Policy(OptimisticAdamUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
parent(parent),
epsilon(ElemType(parent.epsilon)),
beta1(ElemType(parent.beta1)),
beta2(ElemType(parent.beta2)),
iteration(0)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
g.zeros(rows, cols);
// Attempt to detect underflow.
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
}
/**
@@ -115,24 +119,21 @@ class OptimisticAdamUpdate
const GradType& gradient)
{
// Increment the iteration counter variable.
++parent.iteration;
++iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
m *= beta1;
m += (1 - beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * arma::square(gradient);
v *= beta2;
v += (1 - beta2) * square(gradient);
GradType mCorrected = m / (1.0 - std::pow(parent.beta1,
parent.iteration));
GradType vCorrected = v / (1.0 - std::pow(parent.beta2,
parent.iteration));
GradType mCorrected = m / (1 - std::pow(beta1, ElemType(iteration)));
GradType vCorrected = v / (1 - std::pow(beta2, ElemType(iteration)));
GradType update = mCorrected /
(arma::sqrt(vCorrected) + parent.epsilon);
GradType update = mCorrected / (sqrt(vCorrected) + epsilon);
iterate -= (2 * stepSize * update - stepSize * g);
iterate -= (2 * ElemType(stepSize) * update - ElemType(stepSize) * g);
g = std::move(update);
}
@@ -149,6 +150,14 @@ class OptimisticAdamUpdate
// The previous update.
GradType g;
// Parameters converted to the element type of the optimization.
ElemType epsilon;
ElemType beta1;
ElemType beta2;
// The number of iterations.
size_t iteration;
};
private:
@@ -160,9 +169,6 @@ class OptimisticAdamUpdate
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
+487
View File
@@ -0,0 +1,487 @@
/**
* @file agemoea.hpp
* @author Satyam Shukla
*
* AGE-MOEA is a multi-objective optimization algorithm, widely used in
* many real-world applications. AGE-MOEA generates offsprings using
* crossover and mutation and then selects the next generation according
* to non-dominated-sorting and survival score comparison.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_AGEMOEA_AGEMOEA_HPP
#define ENSMALLEN_AGEMOEA_AGEMOEA_HPP
namespace ens {
/**
* This class implements the AGEMOEA algorithm.
*
* The algorithm works by generating a candidate population from a fixed
* starting point. At each stage of optimization, a new population of children
* is generated. This new population along with its predecessor is sorted using
* non-domination as the metric. Following this, the population is further
* segregated in fronts. A new population is generated from these fronts having
* size equal to that of the starting population.
*
* During evolution, two parents are randomly chosen using binary tournament
* selection. A pair of children are generated by crossing over these two
* candidates followed by mutation.
*
* The best front (Pareto optimal) is returned by the Optimize() method.
*
* For more information, see the following:
*
* @code
* @inproceedings{panichella2019adaptive,
* title={An adaptive evolutionary algorithm based on non-euclidean geometry for many-objective optimization},
* author={Panichella, Annibale},
* booktitle={Proceedings of the genetic and evolutionary computation conference},
* pages={595--603},
* year={2019}
* }
* @endcode
*
*/
class AGEMOEA
{
public:
/**
* Constructor for the AGE-MOEA optimizer.
*
* The default values provided over here are not necessarily suitable for a
* given function. Therefore it is highly recommended to adjust the
* parameters according to the problem.
*
* @param populationSize The number of candidates in the population.
* This should be atleast 4 in size and a multiple of 4.
* @param maxGenerations The maximum number of generations allowed for NSGA-II.
* @param crossoverProb The probability that a crossover will occur.
* @param distributionIndex The crowding degree of the mutation.
* @param epsilon The minimum difference required to distinguish between
* candidate solutions.
* @param eta The distance parameters of the crossover distribution.
* @param lowerBound Lower bound of the coordinates of the initial population.
* @param upperBound Upper bound of the coordinates of the initial population.
*/
AGEMOEA(const size_t populationSize = 100,
const size_t maxGenerations = 2000,
const double crossoverProb = 0.6,
const double distributionIndex = 20,
const double epsilon = 1e-6,
const double eta = 20,
const arma::vec& lowerBound = arma::zeros(1, 1),
const arma::vec& upperBound = arma::ones(1, 1));
/**
* Constructor for the AGE-MOEA optimizer. This constructor provides an overload
* to use `lowerBound` and `upperBound` of type double.
*
* The default values provided over here are not necessarily suitable for a
* given function. Therefore it is highly recommended to adjust the
* parameters according to the problem.
*
* @param populationSize The number of candidates in the population.
* This should be atleast 4 in size and a multiple of 4.
* @param maxGenerations The maximum number of generations allowed for NSGA-II.
* @param crossoverProb The probability that a crossover will occur.
* @param distributionIndex The crowding degree of the mutation.
* @param epsilon The minimum difference required to distinguish between
* candidate solutions.
* @param eta The distance parameters of the crossover distribution
* @param lowerBound Lower bound of the coordinates of the initial population.
* @param upperBound Upper bound of the coordinates of the initial population.
*/
AGEMOEA(const size_t populationSize = 100,
const size_t maxGenerations = 2000,
const double crossoverProb = 0.6,
const double distributionIndex = 20,
const double epsilon = 1e-6,
const double eta = 20,
const double lowerBound = 0,
const double upperBound = 1);
/**
* Optimize a set of objectives. The initial population is generated using the
* starting point. The output is the best generated front.
*
* @tparam ArbitraryFunctionType std::tuple of multiple objectives.
* @tparam MatType Type of matrix to optimize.
* @tparam CallbackTypes Types of callback functions.
* @param objectives Vector of objective functions to optimize for.
* @param iterate Starting point.
* @param callbacks Callback functions.
* @return MatType::elem_type The minimum of the accumulated sum over the
* objective values in the best front.
*/
template<typename MatType,
typename... ArbitraryFunctionType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(
std::tuple<ArbitraryFunctionType...>& objectives,
MatType& iterate,
CallbackTypes&&... callbacks);
/**
* Optimize a set of objectives. The initial population is generated using the
* starting point. The output is the best generated front.
*
* @tparam ArbitraryFunctionType std::tuple of multiple objectives.
* @tparam MatType Type of matrix to optimize.
* @tparam CubeType The type of cube used to store the front and Pareto set.
* @tparam CallbackTypes Types of callback functions.
* @param objectives Vector of objective functions to optimize for.
* @param iterate Starting point.
* @param front The generated front.
* @param paretoSet The generated Pareto set.
* @param callbacks Callback functions.
* @return MatType::elem_type The minimum of the accumulated sum over the
* objective values in the best front.
*/
template<typename MatType,
typename CubeType,
typename... ArbitraryFunctionType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(
std::tuple<ArbitraryFunctionType...>& objectives,
MatType& iterate,
CubeType& front,
CubeType& paretoSet,
CallbackTypes&&... callbacks);
//! Get the population size.
size_t PopulationSize() const { return populationSize; }
//! Modify the population size.
size_t& PopulationSize() { return populationSize; }
//! Get the maximum number of generations.
size_t MaxGenerations() const { return maxGenerations; }
//! Modify the maximum number of generations.
size_t& MaxGenerations() { return maxGenerations; }
//! Get the crossover rate.
double CrossoverRate() const { return crossoverProb; }
//! Modify the crossover rate.
double& CrossoverRate() { return crossoverProb; }
//! Retrieve value of the distribution index.
double DistributionIndex() const { return distributionIndex; }
//! Modify the value of the distribution index.
double& DistributionIndex() { return distributionIndex; }
//! Retrieve value of eta.
double Eta() const { return eta; }
//! Modify the value of eta.
double& Eta() { return eta; }
//! Get the tolerance.
double Epsilon() const { return epsilon; }
//! Modify the tolerance.
double& Epsilon() { return epsilon; }
//! Retrieve value of lowerBound.
const arma::vec& LowerBound() const { return lowerBound; }
//! Modify value of lowerBound.
arma::vec& LowerBound() { return lowerBound; }
//! Retrieve value of upperBound.
const arma::vec& UpperBound() const { return upperBound; }
//! Modify value of upperBound.
arma::vec& UpperBound() { return upperBound; }
private:
/**
* Evaluate objectives for the elite population.
*
* @tparam ArbitraryFunctionType std::tuple of multiple function types.
* @tparam MatType Type of matrix to optimize.
* @param population The elite population.
* @param objectives The set of objectives.
* @param calculatedObjectives Vector to store calculated objectives.
*/
template<std::size_t I = 0,
typename InputMatType,
typename ObjectiveMatType,
typename ...ArbitraryFunctionType>
typename std::enable_if<I == sizeof...(ArbitraryFunctionType), void>::type
EvaluateObjectives(std::vector<InputMatType>&,
std::tuple<ArbitraryFunctionType...>&,
std::vector<ObjectiveMatType>&);
template<std::size_t I = 0,
typename InputMatType,
typename ObjectiveMatType,
typename ...ArbitraryFunctionType>
typename std::enable_if<I < sizeof...(ArbitraryFunctionType), void>::type
EvaluateObjectives(std::vector<InputMatType>& population,
std::tuple<ArbitraryFunctionType...>& objectives,
std::vector<ObjectiveMatType>& calculatedObjectives);
/**
* Reproduce candidates from the elite population to generate a new
* population.
*
* @tparam MatType Type of matrix to optimize.
* @param objectives The set of objectives.
* @param lowerBound Lower bound of the coordinates of the initial population.
* @param upperBound Upper bound of the coordinates of the initial population.
*/
template<typename MatType>
void BinaryTournamentSelection(std::vector<MatType>& population,
const MatType& lowerBound,
const MatType& upperBound);
/**
* Crossover two parents to create a pair of new children.
*
* @tparam MatType Type of matrix to optimize.
* @param childA A newly generated candidate.
* @param childB Another newly generated candidate.
* @param parentA First parent from elite population.
* @param parentB Second parent from elite population.
* @param lowerBound The lower bound of the objectives.
* @param upperBound The upper bound of the objectives.
*/
template<typename MatType>
void Crossover(MatType& childA,
MatType& childB,
const MatType& parentA,
const MatType& parentB,
const MatType& lowerBound,
const MatType& upperBound);
/**
* Mutate the coordinates for a candidate.
*
* @tparam MatType Type of matrix to optimize.
* @param candidate The candidate whose coordinates are being modified.
* @param mutationRate The probablity of a mutation to occur.
* @param lowerBound Lower bound of the coordinates of the initial population.
* @param upperBound Upper bound of the coordinates of the initial population.
*/
template<typename MatType>
void Mutate(MatType& candidate,
double mutationRate,
const MatType& lowerBound,
const MatType& upperBound);
/**
* Sort the candidate population using their domination count and the set of
* dominated nodes.
*
* @tparam MatType Type of matrix to optimize.
* @param fronts The population is sorted into these Pareto fronts. The first
* front is the best, the second worse and so on.
* @param ranks The assigned ranks, used for crowding distance based sorting.
* @param calculatedObjectives The previously calculated objectives.
*/
template<typename MatType>
void FastNonDominatedSort(
std::vector<std::vector<size_t> >& fronts,
std::vector<size_t>& ranks,
std::vector<arma::Col<typename MatType::elem_type> >&
calculatedObjectives);
/**
* Operator to check if one candidate Pareto-dominates the other.
*
* A candidate is said to dominate the other if it is at least as good as the
* other candidate for all the objectives and there exists at least one
* objective for which it is strictly better than the other candidate.
*
* @tparam MatType Type of matrix to optimize.
* @param calculatedObjectives The previously calculated objectives.
* @param candidateP The candidate being compared from the elite population.
* @param candidateQ The candidate being compared against.
* @return true if candidateP Pareto dominates candidateQ, otherwise, false.
*/
template<typename MatType>
bool Dominates(
std::vector<arma::Col<typename MatType::elem_type>>& calculatedObjectives,
size_t candidateP,
size_t candidateQ);
/**
* Assigns Survival Score metric for sorting.
*
* @param front The previously generated Pareto fronts.
* @param idealPoint The ideal point of teh first front.
* @param calculatedObjectives The previously calculated objectives.
* @param survivalScore The Survival Score vector to be updated for each
* individual in the population.
* @param normalize The normlization vector of the fronts.
* @param dimension The dimension of the first front.
* @param fNum teh current front index.
*/
template <typename MatType>
void SurvivalScoreAssignment(
const std::vector<size_t>& front,
const arma::Col<typename MatType::elem_type>& idealPoint,
std::vector<arma::Col<typename MatType::elem_type>>& calculatedObjectives,
std::vector<typename MatType::elem_type>& survivalScore,
arma::Col<typename MatType::elem_type>& normalize,
typename MatType::elem_type& dimension,
size_t fNum);
/**
* The operator used in the AGE-MOEA survival score based sorting.
*
* If a candidate has a lower rank then it is preferred.
* Otherwise, if the ranks are equal then the candidate with the larger
* Survival Score is preferred.
*
* @param idxP The index of the first cadidate from the elite population being
* sorted.
* @param idxQ The index of the second cadidate from the elite population
* being sorted.
* @param ranks The previously calculated ranks.
* @param survivalScore The Survival score for each individual in
* the population.
* @return true if the first candidate is preferred, otherwise, false.
*/
template<typename MatType>
bool SurvivalScoreOperator(
size_t idxP,
size_t idxQ,
const std::vector<size_t>& ranks,
const std::vector<typename MatType::elem_type>& survivalScore);
/**
* Normalizes the front given the extreme points in the current front.
*
* @tparam The type of population datapoints.
* @param calculatedObjectives The current population evaluated objectives.
* @param normalization The normalizing vector.
* @param front The previously generated Pareto front.
* @param extreme The indexes of the extreme points in the front.
*/
template <typename MatType>
void NormalizeFront(
std::vector<arma::Col<typename MatType::elem_type>>& calculatedObjectives,
arma::Col<typename MatType::elem_type>& normalization,
const std::vector<size_t>& front,
const arma::Row<size_t>& extreme);
/**
* Get the geometry information p of Lp norm (p > 0).
*
* @param calculatedObjectives The current population evaluated objectives.
* @param front The previously generated Pareto fronts.
* @param extreme The indexes of the extreme points in the front.
* @return The variable p in the Lp norm that best fits the geometry of the
* current front.
*/
template <typename MatType>
typename MatType::elem_type GetGeometry(
std::vector<arma::Col<typename MatType::elem_type> >&
calculatedObjectives,
const std::vector<size_t>& front,
const arma::Row<size_t>& extreme);
/**
* Finds the pairwise Lp distance between all the points in the front.
*
* @param final The current population evaluated objectives.
* @param calculatedObjectives The current population evaluated objectives.
* @param front The front of the current generation.
* @param dimension The calculated dimension of the front.
*/
template <typename MatType>
void PairwiseDistance(
MatType& final,
std::vector<arma::Col<typename MatType::elem_type> >&
calculatedObjectives,
const std::vector<size_t>& front,
const typename MatType::elem_type dimension);
/**
* Finding the indexes of the extreme points in the front.
*
* @param indexes vector containing the slected indexes.
* @param calculatedObjectives The current population objectives.
* @param front The front of the current generation.
*/
template <typename MatType>
void FindExtremePoints(
arma::Row<size_t>& indexes,
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives,
const std::vector<size_t>& front);
/**
* Finding the distance of each point in the front from the line formed
* by pointA and pointB.
*
* @param distance The vector containing the distances of the points in the
* from from the line.
* @param calculatedObjectives Reference to the current population evaluated
* objectives.
* @param front The front of the current generation(indices of population).
* @param pointA The first point on the line.
* @param pointB The second point on the line.
*/
template <typename MatType>
void PointToLineDistance(
arma::Row<typename MatType::elem_type>& distances,
std::vector<arma::Col<typename MatType::elem_type> >&
calculatedObjectives,
const std::vector<size_t>& front,
const arma::Col<typename MatType::elem_type>& pointA,
const arma::Col<typename MatType::elem_type>& pointB);
/**
* Find the Diversity score corresponding the solution S using the selected
* set.
*
* @param selected The current selected set.
* @param pairwiseDistance The current pairwise distance for the whole front.
* @param S The relative index of S being considered within the front.
* @return The diversity score for S which the sum of the two smallest
* elements.
*/
template <typename MatType>
typename MatType::elem_type DiversityScore(std::set<size_t>& selected,
const MatType& pairwiseDistance,
size_t S);
//! The number of objectives being optimised for.
size_t numObjectives;
//! The numbeer of variables used per objectives.
size_t numVariables;
//! The number of candidates in the population.
size_t populationSize;
//! Maximum number of generations before termination criteria is met.
size_t maxGenerations;
//! Probability that crossover will occur.
double crossoverProb;
//! The crowding degree of the mutation. Higher value produces a mutant
//! resembling its parent.
double distributionIndex;
//! The tolerance for termination.
double epsilon;
//! The distance parameters of the crossover distribution.
double eta;
//! Lower bound of the initial swarm.
arma::vec lowerBound;
//! Upper bound of the initial swarm.
arma::vec upperBound;
};
} // namespace ens
// Include implementation.
#include "agemoea_impl.hpp"
#endif
@@ -0,0 +1,848 @@
/**
* @file agemoea_impl.hpp
* @author Satyam Shukla
*
* Implementation of the AGEMOEA algorithm. Used for multi-objective
* optimization problems on arbitrary functions.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more Information.
*/
#ifndef ENSMALLEN_AGEMOEA_AGEMOEA_IMPL_HPP
#define ENSMALLEN_AGEMOEA_AGEMOEA_IMPL_HPP
#include "agemoea.hpp"
#include <assert.h>
namespace ens {
inline AGEMOEA::AGEMOEA(const size_t populationSize,
const size_t maxGenerations,
const double crossoverProb,
const double distributionIndex,
const double epsilon,
const double eta,
const arma::vec& lowerBound,
const arma::vec& upperBound) :
numObjectives(0),
numVariables(0),
populationSize(populationSize),
maxGenerations(maxGenerations),
crossoverProb(crossoverProb),
distributionIndex(distributionIndex),
epsilon(epsilon),
eta(eta),
lowerBound(lowerBound),
upperBound(upperBound)
{ /* Nothing to do here. */ }
inline AGEMOEA::AGEMOEA(const size_t populationSize,
const size_t maxGenerations,
const double crossoverProb,
const double distributionIndex,
const double epsilon,
const double eta,
const double lowerBound,
const double upperBound) :
numObjectives(0),
numVariables(0),
populationSize(populationSize),
maxGenerations(maxGenerations),
crossoverProb(crossoverProb),
distributionIndex(distributionIndex),
epsilon(epsilon),
eta(eta),
lowerBound(lowerBound * arma::ones(1, 1)),
upperBound(upperBound * arma::ones(1, 1))
{ /* Nothing to do here. */ }
//! Optimize the function.
template<typename MatType,
typename... ArbitraryFunctionType,
typename... CallbackTypes>
typename MatType::elem_type AGEMOEA::Optimize(
std::tuple<ArbitraryFunctionType...>& objectives,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
typedef typename ForwardType<MatType>::bcube CubeType;
CubeType paretoFront, paretoSet;
return Optimize(objectives, iterateIn, paretoFront, paretoSet,
std::forward<CallbackTypes>(callbacks)...);
}
//! Optimize the function.
template<typename MatType,
typename CubeType,
typename... ArbitraryFunctionType,
typename... CallbackTypes>
typename MatType::elem_type AGEMOEA::Optimize(
std::tuple<ArbitraryFunctionType...>& objectives,
MatType& iterateIn,
CubeType& paretoFrontIn,
CubeType& paretoSetIn,
CallbackTypes&&... callbacks)
{
// Make sure for evolution to work at least four candidates are present.
if (populationSize < 4 && populationSize % 4 != 0)
{
throw std::logic_error("AGEMOEA::Optimize(): population size should be at"
" least 4, and, a multiple of 4!");
}
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename ForwardType<MatType>::bcol BaseColType;
typedef typename ForwardType<CubeType>::bmat CubeBaseMatType;
BaseMatType& iterate = (BaseMatType&) iterateIn;
// Make sure that we have the methods that we need. Long name...
traits::CheckArbitraryFunctionTypeAPI<ArbitraryFunctionType...,
BaseMatType>();
RequireDenseFloatingPointType<BaseMatType>();
// Check if lower bound is a vector of a single dimension.
if (lowerBound.n_rows == 1)
lowerBound = lowerBound(0, 0) * arma::ones(iterate.n_rows, iterate.n_cols);
// Check if upper bound is a vector of a single dimension.
if (upperBound.n_rows == 1)
upperBound = upperBound(0, 0) * arma::ones(iterate.n_rows, iterate.n_cols);
// Check the dimensions of lowerBound and upperBound.
assert(lowerBound.n_rows == iterate.n_rows && "The dimensions of "
"lowerBound are not the same as the dimensions of iterate.");
assert(upperBound.n_rows == iterate.n_rows && "The dimensions of "
"upperBound are not the same as the dimensions of iterate.");
numObjectives = sizeof...(ArbitraryFunctionType);
numVariables = iterate.n_rows;
// Cache calculated objectives.
std::vector<BaseColType> calculatedObjectives(populationSize);
// Population size reserved to 2 * populationSize + 1 to accommodate
// for the size of intermediate candidate population.
std::vector<BaseMatType> population;
population.reserve(2 * populationSize + 1);
// Pareto fronts, initialized during non-dominated sorting.
// Stores indices of population belonging to a certain front.
std::vector<std::vector<size_t> > fronts;
// Initialised in SurvivalScoreAssignment.
std::vector<ElemType> survivalScore;
// Initialised during non-dominated sorting.
std::vector<size_t> ranks;
//! Useful temporaries for float-like comparisons.
const BaseMatType castedLowerBound = conv_to<BaseMatType>::from(lowerBound);
const BaseMatType castedUpperBound = conv_to<BaseMatType>::from(upperBound);
// Controls early termination of the optimization process.
bool terminate = false;
// Generate the population based on a uniform distribution around the given
// starting point.
for (size_t i = 0; i < populationSize; i++)
{
population.push_back(arma::randu<BaseMatType>(iterate.n_rows,
iterate.n_cols) - ElemType(0.5) + iterate);
// Constrain all genes to be within bounds.
population[i] = min(max(population[i], castedLowerBound),
castedUpperBound);
}
Info << "AGEMOEA initialized successfully. Optimization started." << std::endl;
// Iterate until maximum number of generations is obtained.
Callback::BeginOptimization(*this, objectives, iterate, callbacks...);
for (size_t generation = 1; generation <= maxGenerations && !terminate; generation++)
{
// Create new population of candidate from the present elite population.
// Have P_t, generate G_t using P_t.
BinaryTournamentSelection(population, castedLowerBound, castedUpperBound);
// Evaluate the objectives for the new population.
calculatedObjectives.resize(population.size());
std::fill(calculatedObjectives.begin(), calculatedObjectives.end(),
BaseColType(numObjectives, GetFillType<MatType>::zeros));
EvaluateObjectives(population, objectives, calculatedObjectives);
// Perform fast non dominated sort on P_t G_t.
ranks.resize(population.size());
FastNonDominatedSort<BaseMatType>(fronts, ranks, calculatedObjectives);
arma::Col<ElemType> idealPoint(calculatedObjectives[fronts[0][0]]);
for (size_t index = 1; index < fronts[0].size(); index++)
{
idealPoint = min(idealPoint, calculatedObjectives[fronts[0][index]]);
}
// Perform survival score assignment.
survivalScore.resize(population.size());
std::fill(survivalScore.begin(), survivalScore.end(), 0.);
ElemType dimension;
BaseColType normalize(numObjectives, GetFillType<MatType>::zeros);
for (size_t fNum = 0; fNum < fronts.size(); fNum++)
{
SurvivalScoreAssignment<BaseMatType>(fronts[fNum], idealPoint,
calculatedObjectives, survivalScore, normalize, dimension, fNum);
}
// Sort based on survival score.
std::sort(population.begin(), population.end(),
[this, ranks, survivalScore, population]
(BaseMatType candidateP, BaseMatType candidateQ)
{
size_t idxP{}, idxQ{};
for (size_t i = 0; i < population.size(); i++)
{
if (approx_equal(population[i], candidateP, "absdiff",
ElemType(epsilon)))
idxP = i;
if (approx_equal(population[i], candidateQ, "absdiff",
ElemType(epsilon)))
idxQ = i;
}
return SurvivalScoreOperator<BaseMatType>(idxP, idxQ, ranks,
survivalScore);
}
);
// Yield a new population P_{t+1} of size populationSize.
// Discards unfit population from the R_{t} to yield P_{t+1}.
population.resize(populationSize);
terminate |= Callback::GenerationalStepTaken(*this, objectives, iterate,
calculatedObjectives, fronts, callbacks...);
}
EvaluateObjectives(population, objectives, calculatedObjectives);
// Set the candidates from the Pareto Set as the output.
paretoSetIn.set_size(population[0].n_rows, population[0].n_cols,
population.size());
// The Pareto Set is stored, can be obtained via ParetoSet() getter.
for (size_t solutionIdx = 0; solutionIdx < population.size(); ++solutionIdx)
{
paretoSetIn.slice(solutionIdx) =
conv_to<CubeBaseMatType>::from(population[solutionIdx]);
}
// Set the candidates from the Pareto Front as the output.
paretoFrontIn.set_size(calculatedObjectives[0].n_rows,
calculatedObjectives[0].n_cols, population.size());
for (size_t solutionIdx = 0; solutionIdx < population.size(); ++solutionIdx)
{
paretoFrontIn.slice(solutionIdx) =
conv_to<CubeBaseMatType>::from(calculatedObjectives[solutionIdx]);
}
// Assign iterate to first element of the Pareto Set.
iterate = population[fronts[0][0]];
Callback::EndOptimization(*this, objectives, iterate, callbacks...);
ElemType performance = std::numeric_limits<ElemType>::max();
for (const BaseColType& objective: calculatedObjectives)
if (accu(objective) < performance)
performance = accu(objective);
return performance;
}
//! No objectives to evaluate.
template<std::size_t I,
typename InputMatType,
typename ObjectiveMatType,
typename ...ArbitraryFunctionType>
typename std::enable_if<I == sizeof...(ArbitraryFunctionType), void>::type
AGEMOEA::EvaluateObjectives(
std::vector<InputMatType>&,
std::tuple<ArbitraryFunctionType...>&,
std::vector<ObjectiveMatType>&)
{
// Nothing to do here.
}
//! Evaluate the objectives for the entire population.
template<std::size_t I,
typename InputMatType,
typename ObjectiveMatType,
typename ...ArbitraryFunctionType>
typename std::enable_if<I < sizeof...(ArbitraryFunctionType), void>::type
AGEMOEA::EvaluateObjectives(
std::vector<InputMatType>& population,
std::tuple<ArbitraryFunctionType...>& objectives,
std::vector<ObjectiveMatType>& calculatedObjectives)
{
for (size_t i = 0; i < population.size(); i++)
{
calculatedObjectives[i](I) = std::get<I>(objectives).Evaluate(population[i]);
EvaluateObjectives<I+1, InputMatType, ObjectiveMatType,
ArbitraryFunctionType...>(population, objectives,
calculatedObjectives);
}
}
//! Reproduce and generate new candidates.
template<typename InputMatType>
inline void AGEMOEA::BinaryTournamentSelection(std::vector<InputMatType>& population,
const InputMatType& lowerBound,
const InputMatType& upperBound)
{
std::vector<InputMatType> children;
while (children.size() < population.size())
{
// Choose two random parents for reproduction from the elite population.
size_t indexA = arma::randi<size_t>(
arma::distr_param(0, populationSize - 1));
size_t indexB = arma::randi<size_t>(
arma::distr_param(0, populationSize - 1));
// Make sure that the parents differ.
if (indexA == indexB)
{
if (indexB < populationSize - 1)
indexB++;
else
indexB--;
}
// Initialize the children to the respective parents.
InputMatType childA = population[indexA], childB = population[indexB];
if (arma::randu() <= crossoverProb)
Crossover(childA, childB, population[indexA], population[indexB],
lowerBound, upperBound);
Mutate(childA, 1.0 / static_cast<double>(numVariables),
lowerBound, upperBound);
Mutate(childB, 1.0 / static_cast<double>(numVariables),
lowerBound, upperBound);
// Add the children to the candidate population.
children.push_back(childA);
children.push_back(childB);
}
// Add the candidates to the elite population.
population.insert(std::end(population), std::begin(children),
std::end(children));
}
//! Perform simulated binary crossover (SBX) of genes for the children.
template<typename InputMatType>
inline void AGEMOEA::Crossover(InputMatType& childA,
InputMatType& childB,
const InputMatType& parentA,
const InputMatType& parentB,
const InputMatType& lowerBound,
const InputMatType& upperBound)
{
typedef typename InputMatType::elem_type ElemType;
typedef typename ForwardType<InputMatType>::bcube BaseCubeType;
typedef typename ForwardType<InputMatType>::umat UMatType;
// Generates a child from two parent individuals
// according to the polynomial probability distribution.
BaseCubeType parents(parentA.n_rows,
parentA.n_cols, 2);
parents.slice(0) = parentA;
parents.slice(1) = parentB;
InputMatType current_min = min(parents, 2);
InputMatType current_max = max(parents, 2);
if (accu(parentA - parentB < ElemType(1e-14)))
{
childA = parentA;
childB = parentB;
return;
}
InputMatType current_diff = current_max - current_min;
current_diff.transform( [](ElemType val)
{ return (val < ElemType(1e-10) ? ElemType(1e-10) : val); } );
// Calculating beta used for the final crossover.
InputMatType beta1 = 1 + 2 * (current_min - lowerBound) / current_diff;
InputMatType beta2 = 1 + 2 * (upperBound - current_max) / current_diff;
InputMatType alpha1 = 2 - pow(beta1, -(eta + 1));
InputMatType alpha2 = 2 - pow(beta2, -(eta + 1));
InputMatType us(size(alpha1), GetFillType<InputMatType>::randu);
UMatType mask1 = us > (1 / alpha1);
InputMatType betaq1 = pow(us % alpha1, 1. / (eta + 1));
betaq1 = betaq1 % (mask1 != 1) + pow((1 / (2 - us % alpha1)),
1 / (eta + 1)) % mask1;
UMatType mask2 = us > (1 / alpha2);
InputMatType betaq2 = pow(us % alpha2, 1 / (eta + 1));
betaq2 = betaq2 % (mask1 != 1) + pow((1 / (2 - us % alpha2)),
1 / (eta + 1)) % mask2;
// Variables after the cross over for all of them.
InputMatType c1 = ((current_min + current_max) - betaq1 % current_diff) / 2;
InputMatType c2 = ((current_min + current_max) + betaq2 % current_diff) / 2;
c1 = min(max(c1, lowerBound), upperBound);
c2 = min(max(c2, lowerBound), upperBound);
// Decision for the crossover between the two parents for each variable.
us.randu();
childA = parentA % (us <= ElemType(0.5));
childB = parentB % (us <= ElemType(0.5));
us.randu();
childA = childA + c1 % ((us <= ElemType(0.5)) % (childA == 0));
childA = childA + c2 % ((us > ElemType(0.5)) % (childA == 0));
childB = childB + c2 % ((us <= ElemType(0.5)) % (childB == 0));
childB = childB + c1 % ((us > ElemType(0.5)) % (childB == 0));
}
//! Perform Polynomial mutation of the candidate.
template<typename MatType>
inline void AGEMOEA::Mutate(MatType& candidate,
double mutationRate,
const MatType& lowerBound,
const MatType& upperBound)
{
const size_t numVariables = candidate.n_rows;
for (size_t geneIdx = 0; geneIdx < numVariables; ++geneIdx)
{
// Should this gene be mutated?
if (arma::randu() > mutationRate)
continue;
const double geneRange = upperBound(geneIdx) - lowerBound(geneIdx);
// Normalised distance from the bounds.
const double lowerDelta = (candidate(geneIdx)
- lowerBound(geneIdx)) / geneRange;
const double upperDelta = (upperBound(geneIdx)
- candidate(geneIdx)) / geneRange;
const double mutationPower = 1. / (distributionIndex + 1.0);
const double rand = arma::randu();
double value, perturbationFactor;
if (rand < 0.5)
{
value = 2.0 * rand + (1.0 - 2.0 * rand) *
std::pow(upperDelta, distributionIndex + 1.0);
perturbationFactor = std::pow(value, mutationPower) - 1.0;
}
else
{
value = 2.0 * (1.0 - rand) + 2.0 *(rand - 0.5) *
std::pow(lowerDelta, distributionIndex + 1.0);
perturbationFactor = 1.0 - std::pow(value, mutationPower);
}
candidate(geneIdx) +=
typename MatType::elem_type(perturbationFactor * geneRange);
}
//! Enforce bounds.
candidate = min(max(candidate, lowerBound), upperBound);
}
template <typename MatType>
inline void AGEMOEA::NormalizeFront(
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives,
arma::Col<typename MatType::elem_type>& normalization,
const std::vector<size_t>& front,
const arma::Row<size_t>& extreme)
{
arma::Mat<typename MatType::elem_type> vectorizedObjectives(numObjectives,
front.size());
arma::Mat<typename MatType::elem_type> vectorizedExtremes(numObjectives,
extreme.n_elem);
for (size_t i = 0; i < front.size(); i++)
{
vectorizedObjectives.col(i) = calculatedObjectives[front[i]];
}
for (size_t i = 0; i < extreme.n_elem; i++)
{
vectorizedExtremes.col(i) = calculatedObjectives[front[extreme[i]]];
}
if (front.size() < numObjectives)
{
normalization = arma::max(vectorizedObjectives, 1);
return;
}
arma::Col<typename MatType::elem_type> temp;
arma::uvec unique = arma::find_unique(extreme);
if (extreme.n_elem != unique.n_elem)
{
normalization = arma::max(vectorizedObjectives, 1);
return;
}
arma::Col<typename MatType::elem_type> one(extreme.n_elem, arma::fill::ones);
arma::Col<typename MatType::elem_type> hyperplane(numObjectives, arma::fill::zeros);
try{
hyperplane = arma::solve(
vectorizedExtremes.t(), one);
}
catch(...)
{
normalization = arma::max(vectorizedObjectives, 1);
normalization = normalization + (normalization == 0);
return;
}
if (hyperplane.has_inf() || hyperplane.has_nan() || (arma::accu(hyperplane < 0.0) > 0))
{
normalization = arma::max(vectorizedObjectives, 1);
}
else
{
normalization = 1. / hyperplane;
if (normalization.has_inf() || normalization.has_nan())
{
normalization = arma::max(vectorizedObjectives, 1);
}
}
normalization = normalization + (normalization == 0);
}
template <typename MatType>
inline typename MatType::elem_type AGEMOEA::GetGeometry(
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives,
const std::vector<size_t>& front,
const arma::Row<size_t>& extreme)
{
typedef typename MatType::elem_type ElemType;
arma::Row<ElemType> d;
arma::Col<ElemType> zero(numObjectives, arma::fill::zeros);
arma::Col<ElemType> one(numObjectives, arma::fill::ones);
PointToLineDistance<MatType>(d, calculatedObjectives, front, zero, one);
for (size_t i = 0; i < extreme.size(); i++)
{
d[extreme[i]] = arma::Datum<ElemType>::inf;
}
size_t index = arma::index_min(d);
ElemType avg = accu(calculatedObjectives[front[index]]) / numObjectives;
ElemType p = std::log(ElemType(numObjectives)) / std::log(1 / avg);
if (p <= ElemType(0.1) || std::isnan(p))
p = 1;
return p;
}
//! Pairwise distance for each point in the given front.
template <typename MatType>
inline void AGEMOEA::PairwiseDistance(
MatType& f,
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives,
const std::vector<size_t>& front,
const typename MatType::elem_type dimension)
{
for (size_t i = 0; i < front.size(); i++)
{
for (size_t j = i + 1; j < front.size(); j++)
{
f(i, j) = std::pow(accu(pow(abs(
calculatedObjectives[front[i]] - calculatedObjectives[front[j]]),
dimension)), 1 / dimension);
f(j, i) = f(i, j);
}
}
}
//! Find the index of the of the extreme points in the given front.
template <typename MatType>
void AGEMOEA::FindExtremePoints(
arma::Row<size_t>& indexes,
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives,
const std::vector<size_t>& front)
{
typedef typename MatType::elem_type ElemType;
if (numObjectives >= front.size())
{
indexes = arma::linspace<arma::Row<size_t>>(0, front.size() - 1, front.size());
return;
}
arma::Mat<ElemType> W(numObjectives, numObjectives, arma::fill::eye);
W = W + 1e-6;
std::vector<bool> selected(front.size());
arma::Col<ElemType> z(numObjectives, arma::fill::zeros);
arma::Row<ElemType> dists;
for (size_t i = 0; i < numObjectives; i++)
{
PointToLineDistance<MatType>(dists, calculatedObjectives, front, z, W.col(i));
for (size_t j = 0; j < front.size(); j++)
if (selected[j]){dists[j] = arma::datum::inf;}
indexes[i] = dists.index_min();
selected[dists.index_min()] = true;
}
}
//! Find the distance of a front from a line formed by two points.
template <typename MatType>
void AGEMOEA::PointToLineDistance(
arma::Row<typename MatType::elem_type>& distances,
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives,
const std::vector<size_t>& front,
const arma::Col<typename MatType::elem_type>& pointA,
const arma::Col<typename MatType::elem_type>& pointB)
{
typedef typename MatType::elem_type ElemType;
arma::Row<ElemType> distancesTemp(front.size());
arma::Col<ElemType> ba = pointB - pointA;
arma::Col<ElemType> pa;
for (size_t i = 0; i < front.size(); i++)
{
size_t ind = front[i];
pa = (calculatedObjectives[ind] - pointA);
double t = arma::dot(pa, ba) / arma::dot(ba, ba);
distancesTemp[i] = arma::accu(arma::pow((pa - t * ba), 2));
}
distances = distancesTemp;
}
//! Sort population into Pareto fronts.
template<typename MatType>
inline void AGEMOEA::FastNonDominatedSort(
std::vector<std::vector<size_t> >& fronts,
std::vector<size_t>& ranks,
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives)
{
std::map<size_t, size_t> dominationCount;
std::map<size_t, std::set<size_t> > dominated;
// Reset and initialize fronts.
fronts.clear();
fronts.push_back(std::vector<size_t>());
for (size_t p = 0; p < calculatedObjectives.size(); p++)
{
dominated[p] = std::set<size_t>();
dominationCount[p] = 0;
for (size_t q = 0; q < calculatedObjectives.size(); q++)
{
if (Dominates<MatType>(calculatedObjectives, p, q))
dominated[p].insert(q);
else if (Dominates<MatType>(calculatedObjectives, q, p))
dominationCount[p] += 1;
}
if (dominationCount[p] == 0)
{
ranks[p] = 0;
fronts[0].push_back(p);
}
}
size_t i = 0;
while (!fronts[i].empty())
{
std::vector<size_t> nextFront;
for (size_t p: fronts[i])
{
for (size_t q: dominated[p])
{
dominationCount[q]--;
if (dominationCount[q] == 0)
{
ranks[q] = i + 1;
nextFront.push_back(q);
}
}
}
i++;
fronts.push_back(nextFront);
}
// Remove the empty final set.
fronts.pop_back();
}
//! Check if a candidate Pareto dominates another candidate.
template<typename MatType>
inline bool AGEMOEA::Dominates(
std::vector<arma::Col<typename MatType::elem_type> >& calculatedObjectives,
size_t candidateP,
size_t candidateQ)
{
bool allBetterOrEqual = true;
bool atleastOneBetter = false;
size_t n_objectives = calculatedObjectives[0].n_elem;
for (size_t i = 0; i < n_objectives; i++)
{
// P is worse than Q for the i-th objective function.
if (calculatedObjectives[candidateP](i) > calculatedObjectives[candidateQ](i))
allBetterOrEqual = false;
// P is better than Q for the i-th objective function.
else if (calculatedObjectives[candidateP](i) <
calculatedObjectives[candidateQ](i))
atleastOneBetter = true;
}
return allBetterOrEqual && atleastOneBetter;
}
//! Assign diversity score for a given point and the selected set.
template <typename MatType>
inline typename MatType::elem_type AGEMOEA::DiversityScore(
std::set<size_t>& selected,
const MatType& pairwiseDistance,
size_t S)
{
typedef typename MatType::elem_type ElemType;
ElemType m = arma::datum::inf;
ElemType m1 = arma::datum::inf;
std::set<size_t>::iterator it;
for (it = selected.begin(); it != selected.end(); it++)
{
if (*it == S){ continue; }
if (pairwiseDistance(S, *it) < m)
{
m1 = m;
m = pairwiseDistance(S, *it);
}
else if (pairwiseDistance(S, *it) < m1)
{
m1 = pairwiseDistance(S, *it);
}
}
m1 = (m1 == arma::datum::inf) ? 0 : m1;
m = (m == arma::datum::inf) ? 0 : m;
return m + m1;
}
//! Assign survival score for a front of the population.
template <typename MatType>
inline void AGEMOEA::SurvivalScoreAssignment(
const std::vector<size_t>& front,
const arma::Col<typename MatType::elem_type>& idealPoint,
std::vector<arma::Col<typename MatType::elem_type>>& calculatedObjectives,
std::vector<typename MatType::elem_type>& survivalScore,
arma::Col<typename MatType::elem_type>& normalize,
typename MatType::elem_type& dimension,
size_t fNum)
{
typedef typename MatType::elem_type ElemType;
// Calculations for the first front.
if (fNum == 0)
{
if (front.size() < numObjectives)
{
dimension = 1;
arma::Row<size_t> extreme(numObjectives, arma::fill::zeros);
NormalizeFront<MatType>(calculatedObjectives, normalize, front, extreme);
return;
}
for (size_t index = 0; index < front.size(); index++)
{
calculatedObjectives[front[index]] = calculatedObjectives[front[index]]
- idealPoint;
}
arma::Row<size_t> extreme(numObjectives, arma::fill::zeros);
FindExtremePoints<MatType>(extreme, calculatedObjectives, front);
NormalizeFront<MatType>(calculatedObjectives, normalize, front, extreme);
for (size_t index = 0; index < front.size(); index++)
{
calculatedObjectives[front[index]] = calculatedObjectives[front[index]]
/ normalize;
}
std::set<size_t> selected;
std::set<size_t> remaining;
// Create the selected and remaining sets.
for (size_t index: extreme)
{
selected.insert(index);
survivalScore[front[index]] = arma::Datum<ElemType>::inf;
}
dimension = GetGeometry<MatType>(calculatedObjectives, front, extreme);
for (size_t i = 0; i < front.size(); i++)
{
if (selected.count(i) == 0)
{
remaining.insert(i);
}
}
arma::Mat<ElemType> pairwise(front.size(), front.size(), arma::fill::zeros);
PairwiseDistance<MatType>(pairwise, calculatedObjectives, front, dimension);
arma::Row<typename MatType::elem_type> value(front.size(),
arma::fill::zeros);
// Calculate the diversity and proximity score.
for (size_t i = 0; i < front.size(); i++)
{
pairwise.col(i) = pairwise.col(i) / std::pow(accu(pow(
arma::abs(calculatedObjectives[front[i]]), dimension)), 1 / dimension);
}
while (remaining.size() > 0)
{
std::set<size_t>::iterator it;
value = value.fill(-1);
for (it = remaining.begin(); it != remaining.end(); it++)
{
value[*it] = DiversityScore<MatType>(selected, pairwise, *it);
}
size_t index = arma::index_max(value);
survivalScore[front[index]] = value[index];
selected.insert(index);
remaining.erase(index);
}
}
// Calculations for the other fronts.
else
{
for (size_t i = 0; i < front.size(); i++)
{
calculatedObjectives[front[i]] =
(calculatedObjectives[front[i]]) / normalize;
survivalScore[front[i]] = 1 / std::pow(accu(pow(abs(
calculatedObjectives[front[i]] - idealPoint), dimension)),
1 / dimension);
}
}
}
//! Comparator for survival score based sorting.
template<typename MatType>
inline bool AGEMOEA::SurvivalScoreOperator(
size_t idxP,
size_t idxQ,
const std::vector<size_t>& ranks,
const std::vector<typename MatType::elem_type>& survivalScore)
{
if (ranks[idxP] < ranks[idxQ])
return true;
else if (ranks[idxP] == ranks[idxQ] && survivalScore[idxP] > survivalScore[idxQ])
return true;
return false;
}
} // namespace ens
#endif
@@ -30,7 +30,8 @@ namespace ens {
* documentation on function types included with this distribution or on the
* ensmallen website.
*/
class AugLagrangian
template<typename VecType = arma::vec> // TODO: remove for ensmallen 4.x
class AugLagrangianType
{
public:
/**
@@ -43,13 +44,13 @@ class AugLagrangian
* @param maxIterations Maximum number of iterations of the Augmented
* Lagrangian algorithm. 0 indicates no maximum.
*/
AugLagrangian(const size_t maxIterations = 1000,
const double penaltyThresholdFactor = 0.25,
const double sigmaUpdateFactor = 10.0,
const L_BFGS& lbfgs = L_BFGS());
AugLagrangianType(const size_t maxIterations = 1000,
const double penaltyThresholdFactor = 0.25,
const double sigmaUpdateFactor = 10.0,
const L_BFGS& lbfgs = L_BFGS());
/**
* Optimize the function. The value '1' is used for the initial value of each
* Optimize the function. The value '0' is used for the initial value of each
* Lagrange multiplier. To set the Lagrange multipliers yourself, use the
* other overload of Optimize().
*
@@ -66,7 +67,8 @@ class AugLagrangian
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
typename std::enable_if<IsMatrixType<GradType>::value &&
IsAllNonMatrix<CallbackTypes...>::value, bool>::type
Optimize(LagrangianFunctionType& function,
MatType& coordinates,
CallbackTypes&&... callbacks);
@@ -75,9 +77,10 @@ class AugLagrangian
template<typename LagrangianFunctionType,
typename MatType,
typename... CallbackTypes>
bool Optimize(LagrangianFunctionType& function,
MatType& coordinates,
CallbackTypes&&... callbacks)
typename std::enable_if<IsAllNonMatrix<CallbackTypes...>::value, bool>::type
Optimize(LagrangianFunctionType& function,
MatType& coordinates,
CallbackTypes&&... callbacks)
{
return Optimize<LagrangianFunctionType, MatType, MatType,
CallbackTypes...>(function, coordinates,
@@ -96,29 +99,53 @@ class AugLagrangian
* @tparam CallbackTypes Types of callback functions.
* @param function The function to optimize.
* @param coordinates Output matrix to store the optimized coordinates in.
* @param initLambda Vector of initial Lagrange multipliers. Should have
* length equal to the number of constraints.
* @param initSigma Initial penalty parameter.
* @param lambda Vector containing initial Lagrange multipliers. Should have
* length equal to the number of constraints. This will be overwritten
* with the Lagrange multipliers that are found during optimization.
* @param sigma Initial penalty parameter. This will be overwritten with the
* final penalty value used during optimization.
* @param callbacks Callback functions.
*/
template<typename LagrangianFunctionType,
typename MatType,
typename InVecType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
[[deprecated("use Optimize() with non-const lambda/sigma instead")]]
typename std::enable_if<IsMatrixType<GradType>::value, bool>::type
Optimize(LagrangianFunctionType& function,
MatType& coordinates,
const arma::vec& initLambda,
const InVecType& initLambda,
const double initSigma,
CallbackTypes&&... callbacks)
{
deprecatedLambda = initLambda;
deprecatedSigma = initSigma;
return Optimize(function, coordinates, this->deprecatedLambda,
this->deprecatedSigma,
std::forward<CallbackTypes>(callbacks)...);
}
template<typename LagrangianFunctionType,
typename MatType,
typename InVecType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value, bool>::type
Optimize(LagrangianFunctionType& function,
MatType& coordinates,
InVecType& lambda,
double& sigma,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename LagrangianFunctionType,
typename MatType,
typename... CallbackTypes>
[[deprecated("use Optimize() with non-const lambda/sigma instead")]]
bool Optimize(LagrangianFunctionType& function,
MatType& coordinates,
const arma::vec& initLambda,
const VecType& initLambda,
const double initSigma,
CallbackTypes&&... callbacks)
{
@@ -127,20 +154,39 @@ class AugLagrangian
std::forward<CallbackTypes>(callbacks)...);
}
template<typename LagrangianFunctionType,
typename MatType,
typename InVecType,
typename... CallbackTypes>
bool Optimize(LagrangianFunctionType& function,
MatType& coordinates,
InVecType& lambda,
double& sigma,
CallbackTypes&&... callbacks)
{
return Optimize<LagrangianFunctionType, MatType, InVecType, MatType,
CallbackTypes...>(function, coordinates, lambda, sigma,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the L-BFGS object used for the actual optimization.
const L_BFGS& LBFGS() const { return lbfgs; }
//! Modify the L-BFGS object used for the actual optimization.
L_BFGS& LBFGS() { return lbfgs; }
//! Get the Lagrange multipliers.
const arma::vec& Lambda() const { return lambda; }
[[deprecated("use Optimize() with lambda/sigma parameters instead")]]
const VecType& Lambda() const { return deprecatedLambda; }
//! Modify the Lagrange multipliers (i.e. set them before optimization).
arma::vec& Lambda() { return lambda; }
[[deprecated("use Optimize() with lambda/sigma parameters instead")]]
VecType& Lambda() { return deprecatedLambda; }
//! Get the penalty parameter.
double Sigma() const { return sigma; }
[[deprecated("use Optimize() with lambda/sigma parameters instead")]]
double Sigma() const { return deprecatedSigma; }
//! Modify the penalty parameter.
double& Sigma() { return sigma; }
[[deprecated("use Optimize() with lambda/sigma parameters instead")]]
double& Sigma() { return deprecatedSigma; }
//! Get the maximum iterations
size_t MaxIterations() const { return maxIterations; }
@@ -173,11 +219,11 @@ class AugLagrangian
//! Controls early termination of the optimization process.
bool terminate;
// NOTE: these will be removed in ensmallen 4.x!
//! Lagrange multipliers.
arma::vec lambda;
VecType deprecatedLambda;
//! Penalty parameter.
double sigma;
double deprecatedSigma;
/**
* Internal optimization function: given an initialized AugLagrangianFunction,
@@ -185,27 +231,32 @@ class AugLagrangian
*/
template<typename LagrangianFunctionType,
typename MatType,
typename InVecType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
Optimize(AugLagrangianFunction<LagrangianFunctionType>& augfunc,
typename std::enable_if<IsMatrixType<GradType>::value, bool>::type
Optimize(AugLagrangianFunction<LagrangianFunctionType, InVecType>& augfunc,
MatType& coordinates,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename LagrangianFunctionType,
typename MatType,
typename InVecType,
typename... CallbackTypes>
bool Optimize(AugLagrangianFunction<LagrangianFunctionType>& function,
MatType& coordinates,
CallbackTypes&&... callbacks)
bool Optimize(
AugLagrangianFunction<LagrangianFunctionType, InVecType>& function,
MatType& coordinates,
CallbackTypes&&... callbacks)
{
return Optimize<LagrangianFunctionType, MatType, MatType,
return Optimize<LagrangianFunctionType, MatType, InVecType, MatType,
CallbackTypes...>(function, coordinates,
std::forward<CallbackTypes>(callbacks)...);
}
};
using AugLagrangian = AugLagrangianType<arma::vec>;
} // namespace ens
#include "aug_lagrangian_impl.hpp"
@@ -31,19 +31,10 @@ namespace ens {
*
* @tparam LagrangianFunction Lagrangian function to be used.
*/
template<typename LagrangianFunction>
template<typename LagrangianFunction, typename VecType>
class AugLagrangianFunction
{
public:
/**
* Initialize the AugLagrangianFunction, but don't set the Lagrange
* multipliers or penalty parameters yet. Make sure you set the Lagrange
* multipliers before you use this...
*
* @param function Lagrangian function.
*/
AugLagrangianFunction(LagrangianFunction& function);
/**
* Initialize the AugLagrangianFunction with the given LagrangianFunction,
* Lagrange multipliers, and initial penalty parameter.
@@ -53,8 +44,8 @@ class AugLagrangianFunction
* @param sigma Initial penalty parameter.
*/
AugLagrangianFunction(LagrangianFunction& function,
const arma::vec& lambda,
const double sigma);
VecType& lambda,
double& sigma);
/**
* Evaluate the objective function of the Augmented Lagrangian function, which
* is the standard Lagrangian function evaluation plus a penalty term, which
@@ -81,17 +72,12 @@ class AugLagrangianFunction
*
* @return Initial point.
*/
template<typename MatType = arma::mat>
template<typename MatType>
const MatType& GetInitialPoint() const;
//! Get the Lagrange multipliers.
const arma::vec& Lambda() const { return lambda; }
//! Modify the Lagrange multipliers.
arma::vec& Lambda() { return lambda; }
//! Get sigma (the penalty parameter).
double Sigma() const { return sigma; }
//! Modify sigma (the penalty parameter).
// Get the Lagrange multipliers.
VecType& Lambda() { return lambda; }
// Get the penalty parameter.
double& Sigma() { return sigma; }
//! Get the Lagrangian function.
@@ -104,9 +90,9 @@ class AugLagrangianFunction
LagrangianFunction& function;
//! The Lagrange multipliers.
arma::vec lambda;
VecType& lambda;
//! The penalty parameter.
double sigma;
double& sigma;
};
} // namespace ens
@@ -20,23 +20,11 @@
namespace ens {
// Initialize the AugLagrangianFunction.
template<typename LagrangianFunction>
AugLagrangianFunction<LagrangianFunction>::AugLagrangianFunction(
LagrangianFunction& function) :
function(function),
lambda(function.NumConstraints()),
sigma(10)
{
// Initialize lambda vector to all zeroes.
lambda.zeros();
}
// Initialize the AugLagrangianFunction.
template<typename LagrangianFunction>
AugLagrangianFunction<LagrangianFunction>::AugLagrangianFunction(
template<typename LagrangianFunction, typename VecType>
AugLagrangianFunction<LagrangianFunction, VecType>::AugLagrangianFunction(
LagrangianFunction& function,
const arma::vec& lambda,
const double sigma) :
VecType& lambda,
double& sigma) :
function(function),
lambda(lambda),
sigma(sigma)
@@ -45,9 +33,10 @@ AugLagrangianFunction<LagrangianFunction>::AugLagrangianFunction(
}
// Evaluate the AugLagrangianFunction at the given coordinates.
template<typename LagrangianFunction>
template<typename LagrangianFunction, typename VecType>
template<typename MatType>
typename MatType::elem_type AugLagrangianFunction<LagrangianFunction>::Evaluate(
typename MatType::elem_type
AugLagrangianFunction<LagrangianFunction, VecType>::Evaluate(
const MatType& coordinates) const
{
// The augmented Lagrangian is evaluated as
@@ -63,20 +52,22 @@ typename MatType::elem_type AugLagrangianFunction<LagrangianFunction>::Evaluate(
{
ElemType constraint = function.EvaluateConstraint(i, coordinates);
objective += (-lambda[i] * constraint) +
sigma * std::pow(constraint, 2) / 2;
objective += (-ElemType(lambda[i]) * constraint) +
ElemType(sigma) * std::pow(constraint, ElemType(2)) / 2;
}
return objective;
}
// Evaluate the gradient of the AugLagrangianFunction at the given coordinates.
template<typename LagrangianFunction>
template<typename LagrangianFunction, typename VecType>
template<typename MatType, typename GradType>
void AugLagrangianFunction<LagrangianFunction>::Gradient(
void AugLagrangianFunction<LagrangianFunction, VecType>::Gradient(
const MatType& coordinates,
GradType& gradient) const
{
typedef typename MatType::elem_type ElemType;
// The augmented Lagrangian's gradient is evaluted as
// f'(x) + {(-lambda_i + sigma * c_i(x)) * c'_i(x)} for all constraints
gradient.zeros();
@@ -89,16 +80,17 @@ void AugLagrangianFunction<LagrangianFunction>::Gradient(
// Now calculate scaling factor and add to existing gradient.
GradType tmpGradient;
tmpGradient = (-lambda[i] + sigma *
tmpGradient = (ElemType(-lambda[i]) + ElemType(sigma) *
function.EvaluateConstraint(i, coordinates)) * constraintGradient;
gradient += tmpGradient;
}
}
// Get the initial point.
template<typename LagrangianFunction>
template<typename LagrangianFunction, typename VecType>
template<typename MatType>
const MatType& AugLagrangianFunction<LagrangianFunction>::GetInitialPoint()
const MatType&
AugLagrangianFunction<LagrangianFunction, VecType>::GetInitialPoint()
const
{
return function.template GetInitialPoint<MatType>();
@@ -19,70 +19,90 @@
namespace ens {
inline AugLagrangian::AugLagrangian(const size_t maxIterations,
const double penaltyThresholdFactor,
const double sigmaUpdateFactor,
const L_BFGS& lbfgs) :
template<typename VecType>
inline AugLagrangianType<VecType>::AugLagrangianType(
const size_t maxIterations,
const double penaltyThresholdFactor,
const double sigmaUpdateFactor,
const L_BFGS& lbfgs) :
maxIterations(maxIterations),
penaltyThresholdFactor(penaltyThresholdFactor),
sigmaUpdateFactor(sigmaUpdateFactor),
lbfgs(lbfgs),
terminate(false),
sigma(0.0)
deprecatedSigma(0.0)
{
}
template<typename VecType>
template<typename LagrangianFunctionType,
typename MatType,
typename InVecType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
AugLagrangian::Optimize(LagrangianFunctionType& function,
MatType& coordinates,
const arma::vec& initLambda,
const double initSigma,
CallbackTypes&&... callbacks)
typename std::enable_if<IsMatrixType<GradType>::value, bool>::type
AugLagrangianType<VecType>::Optimize(
LagrangianFunctionType& function,
MatType& coordinates,
InVecType& lambda,
double& sigma,
CallbackTypes&&... callbacks)
{
lambda = initLambda;
sigma = initSigma;
AugLagrangianFunction<LagrangianFunctionType> augfunc(function,
lambda, sigma);
AugLagrangianFunction<LagrangianFunctionType, InVecType> augfunc(
function, lambda, sigma);
return Optimize(augfunc, coordinates, callbacks...);
}
template<typename VecType>
template<typename LagrangianFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
AugLagrangian::Optimize(LagrangianFunctionType& function,
MatType& coordinates,
CallbackTypes&&... callbacks)
typename std::enable_if<IsMatrixType<GradType>::value &&
IsAllNonMatrix<CallbackTypes...>::value, bool>::type
AugLagrangianType<VecType>::Optimize(LagrangianFunctionType& function,
MatType& coordinates,
CallbackTypes&&... callbacks)
{
typedef typename ForwardType<MatType>::bvec InVecType;
// If the user did not specify the right size for sigma and lambda, we will
// use defaults.
if (!lambda.is_empty())
// TODO: remove this when ensmallen 4.x is released!
if (!deprecatedLambda.is_empty())
{
AugLagrangianFunction<LagrangianFunctionType> augfunc(function, lambda,
sigma);
return Optimize(augfunc, coordinates, callbacks...);
InVecType lambda(conv_to<InVecType>::from(deprecatedLambda));
AugLagrangianFunction<LagrangianFunctionType, InVecType> augfunc(function,
lambda, deprecatedSigma);
const bool result = Optimize(augfunc, coordinates, callbacks...);
deprecatedLambda = conv_to<VecType>::from(lambda);
return result;
}
else
{
AugLagrangianFunction<LagrangianFunctionType> augfunc(function);
// Use default values.
InVecType lambda(function.NumConstraints());
lambda.zeros();
double sigma = 10;
AugLagrangianFunction<LagrangianFunctionType, InVecType> augfunc(
function, lambda, sigma);
return Optimize(augfunc, coordinates, callbacks...);
}
}
template<typename VecType>
template<typename LagrangianFunctionType,
typename MatType,
typename InVecType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
AugLagrangian::Optimize(
AugLagrangianFunction<LagrangianFunctionType>& augfunc,
typename std::enable_if<IsMatrixType<GradType>::value, bool>::type
AugLagrangianType<VecType>::Optimize(
AugLagrangianFunction<LagrangianFunctionType, InVecType>& augfunc,
MatType& coordinatesIn,
CallbackTypes&&... callbacks)
{
@@ -110,15 +130,16 @@ AugLagrangian::Optimize(
// Convergence tolerance---depends on the epsilon of the type we are using for
// optimization.
ElemType tolerance = 1e3 * std::numeric_limits<ElemType>::epsilon();
ElemType tolerance = 1000 * std::numeric_limits<ElemType>::epsilon();
// Then, calculate the current penalty.
ElemType penalty = 0;
for (size_t i = 0; i < function.NumConstraints(); i++)
{
const ElemType p = std::pow(function.EvaluateConstraint(i, coordinates), 2);
Callback::EvaluateConstraint(*this, function, coordinates, i, p,
callbacks...);
const ElemType p = std::pow(function.EvaluateConstraint(i, coordinates),
ElemType(2));
terminate |= Callback::EvaluateConstraint(*this, function, coordinates, i,
p, callbacks...);
penalty += p;
}
@@ -129,21 +150,19 @@ AugLagrangian::Optimize(
// The odd comparison allows user to pass maxIterations = 0 (i.e. no limit on
// number of iterations).
size_t it;
terminate |= Callback::BeginOptimization(*this, function, coordinates,
callbacks...);
Callback::BeginOptimization(*this, function, coordinates, callbacks...);
for (it = 0; it != (maxIterations - 1) && !terminate; it++)
{
Info << "AugLagrangian on iteration " << it
<< ", starting with objective " << lastObjective << "." << std::endl;
if (!lbfgs.Optimize(augfunc, coordinates, callbacks...))
Info << "L-BFGS reported an error during optimization."
<< std::endl;
Info << "Done with L-BFGS: " << coordinates << "\n";
Info << "L-BFGS reported an error during optimization." << std::endl;
Info << "Done with L-BFGS." << std::endl;
const ElemType objective = function.Evaluate(coordinates);
Callback::Evaluate(*this, function, coordinates, objective,
terminate |= Callback::Evaluate(*this, function, coordinates, objective,
callbacks...);
// Check if we are done with the entire optimization (the threshold we are
@@ -151,9 +170,6 @@ AugLagrangian::Optimize(
if (std::abs(lastObjective - objective) < tolerance &&
augfunc.Sigma() > 500000)
{
lambda = std::move(augfunc.Lambda());
sigma = augfunc.Sigma();
Callback::EndOptimization(*this, function, coordinates, callbacks...);
return true;
}
@@ -169,15 +185,18 @@ AugLagrangian::Optimize(
for (size_t i = 0; i < function.NumConstraints(); i++)
{
const ElemType p = std::pow(function.EvaluateConstraint(i, coordinates),
2);
Callback::EvaluateConstraint(*this, function, coordinates, i, p,
callbacks...);
ElemType(2));
terminate |= Callback::EvaluateConstraint(*this, function, coordinates, i,
p, callbacks...);
penalty += p;
}
Info << "Penalty is " << penalty << " (threshold "
<< penaltyThreshold << ")." << std::endl;
Info << "Penalty is " << penalty << " (threshold " << penaltyThreshold
<< ")." << std::endl;
if (terminate)
break;
if (penalty < penaltyThreshold) // We update lambda.
{
@@ -186,15 +205,15 @@ AugLagrangian::Optimize(
for (size_t i = 0; i < function.NumConstraints(); i++)
{
const ElemType p = function.EvaluateConstraint(i, coordinates);
Callback::EvaluateConstraint(*this, function, coordinates, i, p,
callbacks...);
terminate |= Callback::EvaluateConstraint(*this, function, coordinates,
i, p, callbacks...);
augfunc.Lambda()[i] -= augfunc.Sigma() * p;
augfunc.Lambda()[i] -= ElemType(augfunc.Sigma()) * p;
}
// We also update the penalty threshold to be a factor of the current
// penalty.
penaltyThreshold = penaltyThresholdFactor * penalty;
penaltyThreshold = ElemType(penaltyThresholdFactor) * penalty;
Info << "Lagrange multiplier estimates updated." << std::endl;
}
else
@@ -207,7 +226,7 @@ AugLagrangian::Optimize(
Warn << "AugLagrangian::Optimize(): sigma too large for element type; "
<< "terminating." << std::endl;
Callback::EndOptimization(*this, function, coordinates, callbacks...);
return false;
return true;
}
}
@@ -69,6 +69,8 @@ class AdaptiveStepsize
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
// Create the instantiated object.
Policy(AdaptiveStepsize& parent) : parent(parent) { }
@@ -104,7 +106,7 @@ class AdaptiveStepsize
backtrackingBatchSize);
// Update the iterate.
iterate -= stepSize * gradient;
iterate -= ElemType(stepSize) * gradient;
// Update Gradient & calculate curvature of quadratic approximation.
GradType functionGradient(iterate.n_rows, iterate.n_cols);
@@ -132,8 +134,8 @@ class AdaptiveStepsize
delta0 = delta1 + (functionGradient - delta1) / k;
// Compute sample variance.
vB += arma::norm(functionGradient - delta1, 2.0) *
arma::norm(functionGradient - delta0, 2.0);
vB += norm(functionGradient - delta1, 2.0) *
norm(functionGradient - delta0, 2.0);
delta1 = delta0;
gradient += functionGradient;
@@ -145,13 +147,13 @@ class AdaptiveStepsize
// Update sample variance & norm of the gradient.
sampleVariance = vB;
gradientNorm = std::pow(arma::norm(gradient / backtrackingBatchSize, 2),
gradientNorm = std::pow(norm(gradient / backtrackingBatchSize, 2),
2.0);
// Compute curvature.
double v = arma::trace(arma::trans(iterate - iteratePrev) *
double v = trace(trans(iterate - iteratePrev) *
(gradient - gradPrevIterate)) /
std::pow(arma::norm(iterate - iteratePrev, 2), 2.0);
std::pow(norm(iterate - iteratePrev, 2), 2.0);
// Update previous iterate.
iteratePrev = iterate;
@@ -205,12 +207,10 @@ class AdaptiveStepsize
const size_t offset,
const size_t backtrackingBatchSize)
{
typedef typename MatType::elem_type ElemType;
ElemType overallObjective = function.Evaluate(iterate,
offset, backtrackingBatchSize);
MatType iterateUpdate = iterate - (stepSize * gradient);
MatType iterateUpdate = iterate - (ElemType(stepSize) * gradient);
ElemType overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
backtrackingBatchSize);
@@ -220,7 +220,7 @@ class AdaptiveStepsize
{
stepSize *= parent.backtrackStepSize;
iterateUpdate = iterate - (stepSize * gradient);
iterateUpdate = iterate - (ElemType(stepSize) * gradient);
overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
backtrackingBatchSize);
}
@@ -60,6 +60,8 @@ class BacktrackingLineSearch
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
// Instantiate the policy with the given parent.
Policy(BacktrackingLineSearch& parent) : parent(parent) { }
@@ -94,12 +96,10 @@ class BacktrackingLineSearch
if (reset)
stepSize *= 2;
typedef typename MatType::elem_type ElemType;
ElemType overallObjective = function.Evaluate(iterate, offset,
backtrackingBatchSize);
MatType iterateUpdate = iterate - (stepSize * gradient);
MatType iterateUpdate = iterate - (ElemType(stepSize) * gradient);
ElemType overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
backtrackingBatchSize);
@@ -109,7 +109,7 @@ class BacktrackingLineSearch
{
stepSize /= 2;
iterateUpdate = iterate - (stepSize * gradient);
iterateUpdate = iterate - (ElemType(stepSize) * gradient);
overallObjectiveUpdate = function.Evaluate(iterateUpdate,
offset, backtrackingBatchSize);
}
@@ -125,7 +125,7 @@ class BigBatchSGD
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
@@ -50,8 +50,8 @@ template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
BigBatchSGD<UpdatePolicyType>::Optimize(
SeparableFunctionType& function,
MatType& iterateIn,
@@ -104,7 +104,7 @@ BigBatchSGD<UpdatePolicyType>::Optimize(
BaseGradType functionGradient(iterate.n_rows, iterate.n_cols);
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
terminate |= Callback::BeginOptimization(*this, f, iterate, callbacks...);
Callback::BeginOptimization(*this, f, iterate, callbacks...);
for (size_t i = 0; i < actualMaxIterations && !terminate;
/* incrementing done manually */)
{
@@ -137,13 +137,13 @@ BigBatchSGD<UpdatePolicyType>::Optimize(
delta0 = delta1 + (functionGradient - delta1) / k;
// Compute sample variance.
vB += arma::norm(functionGradient - delta1, 2.0) *
arma::norm(functionGradient - delta0, 2.0);
vB += norm(functionGradient - delta1, 2.0) *
norm(functionGradient - delta0, 2.0);
delta1 = delta0;
gradient += functionGradient;
}
double gB = std::pow(arma::norm(gradient / effectiveBatchSize, 2), 2.0);
double gB = std::pow(norm(gradient / effectiveBatchSize, 2), 2.0);
// Reset the batch size update process counter.
reset = false;
@@ -174,13 +174,13 @@ BigBatchSGD<UpdatePolicyType>::Optimize(
delta0 = delta1 + (functionGradient - delta1) / (k + 1);
// Compute sample variance.
vB += arma::norm(functionGradient - delta1, 2.0) *
arma::norm(functionGradient - delta0, 2.0);
vB += norm(functionGradient - delta1, 2.0) *
norm(functionGradient - delta0, 2.0);
delta1 = delta0;
gradient += functionGradient;
}
gB = std::pow(arma::norm(gradient / (batchSize + batchOffset), 2), 2.0);
gB = std::pow(norm(gradient / (batchSize + batchOffset), 2), 2.0);
// Update the batchSize.
batchSize += batchOffset;
@@ -191,12 +191,15 @@ BigBatchSGD<UpdatePolicyType>::Optimize(
}
}
if (terminate)
break;
instUpdatePolicy.As<InstUpdatePolicyType>().Update(f, stepSize, iterate,
gradient, gB, vB, currentFunction, batchSize, effectiveBatchSize,
reset);
// Update the iterate.
iterate -= stepSize * gradient;
iterate -= ElemType(stepSize) * gradient;
terminate |= Callback::StepTaken(*this, f, iterate, callbacks...);
const ElemType objective = f.Evaluate(iterate, currentFunction,
@@ -229,9 +232,7 @@ BigBatchSGD<UpdatePolicyType>::Optimize(
return overallObjective;
}
if (std::abs(lastObjective - overallObjective) < tolerance ||
Callback::BeginEpoch(*this, f, iterate, epoch, overallObjective,
callbacks...))
if (std::abs(lastObjective - overallObjective) < tolerance)
{
Info << "Big-batch SGD: minimized within tolerance " << tolerance
<< "; terminating optimization." << std::endl;
@@ -240,18 +241,27 @@ BigBatchSGD<UpdatePolicyType>::Optimize(
return overallObjective;
}
// Reset the counter variables.
lastObjective = overallObjective;
overallObjective = 0;
currentFunction = 0;
terminate |= Callback::BeginEpoch(*this, f, iterate, epoch,
overallObjective, callbacks...);
// Reset the counter variables if we will continue.
if (i != actualMaxIterations)
{
lastObjective = overallObjective;
overallObjective = 0;
currentFunction = 0;
}
if (shuffle) // Determine order of visitation.
f.Shuffle();
}
}
Info << "Big-batch SGD: maximum iterations (" << maxIterations << ") "
<< "reached; terminating optimization." << std::endl;
if (!terminate)
{
Info << "Big-batch SGD: maximum iterations (" << maxIterations << ") "
<< "reached; terminating optimization." << std::endl;
}
// Calculate final objective if exactObjective is set to true.
if (exactObjective)
@@ -263,7 +273,9 @@ BigBatchSGD<UpdatePolicyType>::Optimize(
const ElemType objective = f.Evaluate(iterate, i, effectiveBatchSize);
overallObjective += objective;
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
// The optimization is finished, so we don't need to care what the
// callback returns.
(void) Callback::Evaluate(*this, f, iterate, objective, callbacks...);
}
}
+290 -82
View File
@@ -20,32 +20,35 @@ namespace ens {
* Callbacks are a set of functions that can be applied at given stages of the
* optimization process. The following callbacks are available:
*
* - Evaluate(optimizer, function, coordinates, objective):
* - bool Evaluate(optimizer, function, coordinates, objective):
* called after any call to Evaluate().
*
* - StepTaken(optimizer, function, coordinates):
* - bool StepTaken(optimizer, function, coordinates):
* called after any step is taken.
*
* - Gradient(optimizer, function, coordinates, gradient):
* - bool Gradient(optimizer, function, coordinates, gradient):
* called whenever the gradient is computed.
*
* - BeginEpoch(optimizer, function, coordinates, epoch, objective):
* - bool BeginEpoch(optimizer, function, coordinates, epoch, objective):
* called at the beginning of a pass over the data. The objective may be
* exact or an estimate depending on exactObjective's value.
*
* - EvaluateConstraint(optimizer, function, coordinates, constraint,
* constraintValue):
* - bool EvaluateConstraint(optimizer, function, coordinates, constraint,
* constraintValue):
* called after any call to EvaluateConstraint().
*
* - GradientConstraint(optimizer, function, coordinates, constraint,
* constraintGradient):
* - bool GradientConstraint(optimizer, function, coordinates, constraint,
* constraintGradient):
* called after any call to GradientConstraint().
*
* - BeginOptimization(optimizer, function, coordinates):
* - void BeginOptimization(optimizer, function, coordinates):
* called at the beginning of the optimization.
*
* - EndOptimization(optimizer, function, coordinates):
* - void EndOptimization(optimizer, function, coordinates):
* called at the end of the optimization.
*
* If true is returned to any of the bool-type callbacks, the optimization will
* be terminated before any more steps are taken.
*/
class Callback
{
@@ -64,14 +67,14 @@ class Callback
typename MatType>
static typename std::enable_if<
callbacks::traits::HasBeginOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasBool,
bool>::type
CallbackType, OptimizerType, FunctionType, MatType>::value,
void>::type
BeginOptimizationFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates)
{
return const_cast<CallbackType&>(callback).BeginOptimization(optimizer,
(void) const_cast<CallbackType&>(callback).BeginOptimization(optimizer,
function, coordinates);
}
@@ -80,32 +83,14 @@ class Callback
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasBeginOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasVoid,
bool>::type
BeginOptimizationFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates)
{
const_cast<CallbackType&>(callback).BeginOptimization(optimizer, function,
coordinates);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasBeginOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasNone,
bool>::type
!callbacks::traits::HasBeginOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
void>::type
BeginOptimizationFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ return false; }
{ /* Nothing to do. */ }
/**
* Iterate over the callbacks and invoke the BeginOptimization() callback if
@@ -119,18 +104,28 @@ class Callback
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename CallbackType,
typename... CallbackTypes>
static bool BeginOptimization(OptimizerType& optimizer,
static void BeginOptimization(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates,
CallbackTypes&... callbacks)
CallbackType& callback,
CallbackTypes&... otherCallbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)std::initializer_list<bool>{ result =
result || Callback::BeginOptimizationFunction(callbacks, optimizer,
function, coordinates)... };
return result;
Callback::BeginOptimizationFunction(callback, optimizer, function,
coordinates);
Callback::BeginOptimization(optimizer, function, coordinates,
otherCallbacks...);
}
template<typename OptimizerType,
typename FunctionType,
typename MatType>
static void BeginOptimization(OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{
// Base case... no callbacks left. Nothing to do.
}
/**
@@ -147,28 +142,29 @@ class Callback
typename MatType>
static typename std::enable_if<callbacks::traits::HasEndOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
void>::type
EndOptimizationFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates)
{
return (const_cast<CallbackType&>(callback).EndOptimization(
optimizer, function, coordinates), false);
(void) const_cast<CallbackType&>(callback).EndOptimization( optimizer,
function, coordinates);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<!callbacks::traits::HasEndOptimizationSignature<
static typename std::enable_if<
!callbacks::traits::HasEndOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
void>::type
EndOptimizationFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ return false; }
{ /* Nothing to do. */ }
/**
* Iterate over the callbacks and invoke the EndOptimization() callback if it
@@ -182,18 +178,28 @@ class Callback
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename CallbackType,
typename... CallbackTypes>
static bool EndOptimization(OptimizerType& optimizer,
static void EndOptimization(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates,
CallbackTypes&... callbacks)
CallbackType& callback,
CallbackTypes&... otherCallbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)std::initializer_list<bool>{ result =
result || Callback::EndOptimizationFunction(callbacks, optimizer,
function, coordinates)... };
return result;
Callback::EndOptimizationFunction(callback, optimizer, function,
coordinates);
Callback::EndOptimization(optimizer, function, coordinates,
otherCallbacks...);
}
template<typename OptimizerType,
typename FunctionType,
typename MatType>
static void EndOptimization(OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{
// Base case... no callbacks left. Nothing to do.
}
/**
@@ -210,7 +216,7 @@ class Callback
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasEvaluateSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
CallbackType, OptimizerType, FunctionType, MatType>::hasBool,
bool>::type
EvaluateFunction(CallbackType& callback,
OptimizerType& optimizer,
@@ -218,16 +224,34 @@ class Callback
const MatType& coordinates,
const double objective)
{
return (const_cast<CallbackType&>(callback).Evaluate(
optimizer, function, coordinates, objective), false);
return const_cast<CallbackType&>(callback).Evaluate(optimizer, function,
coordinates, objective);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<!callbacks::traits::HasEvaluateSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
static typename std::enable_if<callbacks::traits::HasEvaluateSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasVoid,
bool>::type
EvaluateFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const double objective)
{
const_cast<CallbackType&>(callback).Evaluate(optimizer, function,
coordinates, objective);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasEvaluateSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasNone,
bool>::type
EvaluateFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
@@ -280,7 +304,7 @@ class Callback
typename MatType>
static typename std::enable_if<
callbacks::traits::HasEvaluateConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
CallbackType, OptimizerType, FunctionType, MatType>::hasBool,
bool>::type
EvaluateConstraintFunction(CallbackType& callback,
OptimizerType& optimizer,
@@ -289,8 +313,8 @@ class Callback
const size_t constraint,
const double constraintValue)
{
return (const_cast<CallbackType&>(callback).EvaluateConstraint(
optimizer, function, coordinates, constraint, constraintValue), false);
return const_cast<CallbackType&>(callback).EvaluateConstraint(
optimizer, function, coordinates, constraint, constraintValue);
}
template<typename CallbackType,
@@ -298,8 +322,28 @@ class Callback
typename FunctionType,
typename MatType>
static typename std::enable_if<
!callbacks::traits::HasEvaluateConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
callbacks::traits::HasEvaluateConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasVoid,
bool>::type
EvaluateConstraintFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraint,
const double constraintValue)
{
const_cast<CallbackType&>(callback).EvaluateConstraint(
optimizer, function, coordinates, constraint, constraintValue);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasEvaluateConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasNone,
bool>::type
EvaluateConstraintFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
@@ -356,7 +400,7 @@ class Callback
typename MatType,
typename GradType>
static typename std::enable_if<callbacks::traits::HasGradientSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::value,
CallbackType, OptimizerType, FunctionType, MatType, GradType>::hasBool,
bool>::type
GradientFunction(CallbackType& callback,
OptimizerType& optimizer,
@@ -364,8 +408,8 @@ class Callback
const MatType& coordinates,
GradType& gradient)
{
return (const_cast<CallbackType&>(callback).Gradient(
optimizer, function, coordinates, gradient), false);
return const_cast<CallbackType&>(callback).Gradient(optimizer, function,
coordinates, gradient);
}
template<typename CallbackType,
@@ -373,8 +417,27 @@ class Callback
typename FunctionType,
typename MatType,
typename GradType>
static typename std::enable_if<!callbacks::traits::HasGradientSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::value,
static typename std::enable_if<callbacks::traits::HasGradientSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::hasVoid,
bool>::type
GradientFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
GradType& gradient)
{
const_cast<CallbackType&>(callback).Gradient(
optimizer, function, coordinates, gradient);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
static typename std::enable_if<callbacks::traits::HasGradientSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::hasNone,
bool>::type
GradientFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
@@ -427,7 +490,7 @@ class Callback
typename GradType>
static typename std::enable_if<
callbacks::traits::HasGradientConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::value,
CallbackType, OptimizerType, FunctionType, MatType, GradType>::hasBool,
bool>::type
GradientConstraintFunction(CallbackType& callback,
OptimizerType& optimizer,
@@ -436,8 +499,8 @@ class Callback
const size_t constraint,
GradType& gradient)
{
return (const_cast<CallbackType&>(callback).GradientConstraint(
optimizer, function, coordinates, constraint, gradient), false);
return const_cast<CallbackType&>(callback).GradientConstraint(optimizer,
function, coordinates, constraint, gradient);
}
template<typename CallbackType,
@@ -446,8 +509,29 @@ class Callback
typename MatType,
typename GradType>
static typename std::enable_if<
!callbacks::traits::HasGradientConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::value,
callbacks::traits::HasGradientConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::hasVoid,
bool>::type
GradientConstraintFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraint,
GradType& gradient)
{
const_cast<CallbackType&>(callback).GradientConstraint(
optimizer, function, coordinates, constraint, gradient);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
static typename std::enable_if<
callbacks::traits::HasGradientConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::hasNone,
bool>::type
GradientConstraintFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
@@ -539,7 +623,7 @@ class Callback
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasBeginEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value, bool>::type
CallbackType, OptimizerType, FunctionType, MatType>::hasBool, bool>::type
BeginEpochFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
@@ -547,16 +631,34 @@ class Callback
const size_t epoch,
const double objective)
{
return (const_cast<CallbackType&>(callback).BeginEpoch(
optimizer, function, coordinates, epoch, objective), false);
return const_cast<CallbackType&>(callback).BeginEpoch(
optimizer, function, coordinates, epoch, objective);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<!callbacks::traits::HasBeginEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value, bool>::type
static typename std::enable_if<callbacks::traits::HasBeginEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasVoid, bool>::type
BeginEpochFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective)
{
const_cast<CallbackType&>(callback).BeginEpoch(
optimizer, function, coordinates, epoch, objective);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasBeginEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasNone, bool>::type
BeginEpochFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
@@ -769,6 +871,112 @@ class Callback
function, coordinates)... };
return result;
}
/**
* Invoke the GenerationalStepTaken() callback if it exists.
* Specialization for MultiObjective case.
*
* @param callback The callback to call.
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objectives The set of calculated objectives so far.
* @param frontIndices The indices of the members belonging to Pareto Front.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename ObjectivesVecType,
typename IndicesType>
static typename std::enable_if<
callbacks::traits::HasGenerationalStepTakenSignature<
CallbackType, OptimizerType, FunctionType, MatType, ObjectivesVecType,
IndicesType>::hasBool, bool>::type
GenerationalStepTakenFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates,
ObjectivesVecType& objectives,
IndicesType& frontIndices)
{
return const_cast<CallbackType&>(callback).GenerationalStepTaken(
optimizer, function, coordinates, objectives, frontIndices);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename ObjectivesVecType,
typename IndicesType>
static typename std::enable_if<
callbacks::traits::HasGenerationalStepTakenSignature<
CallbackType, OptimizerType, FunctionType, MatType, ObjectivesVecType,
IndicesType>::hasVoid, bool>::type
GenerationalStepTakenFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates,
ObjectivesVecType& objectives,
IndicesType& frontIndices)
{
const_cast<CallbackType&>(callback).GenerationalStepTaken(
optimizer, function, coordinates, objectives, frontIndices);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename ObjectivesVecType,
typename IndicesType>
static typename std::enable_if<
callbacks::traits::HasGenerationalStepTakenSignature<
CallbackType, OptimizerType, FunctionType, MatType, ObjectivesVecType,
IndicesType>::hasNone, bool>::type
GenerationalStepTakenFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */,
ObjectivesVecType& /* objectives */,
IndicesType& /* frontIndices */)
{ return false; }
/**
* Iterate over the callbacks and invoke the GenerationalStepTaken() callback if it
* exists.
*
* Specialization for MultiObjective case.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objectives The set of calculated objectives so far.
* @param frontIndices The indices of the members belonging to Pareto Front.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename ObjectivesVecType,
typename IndicesType,
typename MatType,
typename ...CallbackTypes>
static bool GenerationalStepTaken(OptimizerType& optimizer,
FunctionType& functions,
MatType& coordinates,
ObjectivesVecType& objectives,
IndicesType& frontIndices,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)std::initializer_list<bool>{ result = result ||
Callback::GenerationalStepTakenFunction(callbacks, optimizer, functions,
coordinates, objectives, frontIndices)... };
return result;
}
};
} // namespace ens
@@ -32,9 +32,9 @@ class EarlyStopAtMinLossType
* @param patienceIn The number of epochs to wait after the minimum loss has
* been reached or no improvement has been made (Default: 10).
*/
EarlyStopAtMinLossType<MatType>(const size_t patienceIn = 10) :
callbackUsed(false),
patience(patienceIn),
EarlyStopAtMinLossType(const size_t patienceIn = 10) :
callbackUsed(false),
patience(patienceIn),
bestObjective(std::numeric_limits<double>::max()),
steps(0)
{ /* Nothing to do here */ }
@@ -47,13 +47,13 @@ class EarlyStopAtMinLossType
* @param patienceIn The number of epochs to wait after the minimum loss has
* been reached or no improvement has been made (Default: 10).
*/
EarlyStopAtMinLossType<MatType>(
EarlyStopAtMinLossType(
std::function<double(const MatType&)> func,
const size_t patienceIn = 10)
: callbackUsed(true),
patience(patienceIn),
: callbackUsed(true),
patience(patienceIn),
bestObjective(std::numeric_limits<double>::max()),
steps(0),
steps(0),
localFunc(func)
{
// Nothing to do here
@@ -78,7 +78,7 @@ class EarlyStopAtMinLossType
if (callbackUsed)
{
objective = localFunc(coordinates);
}
}
if (objective < bestObjective)
{
@@ -98,7 +98,8 @@ class EarlyStopAtMinLossType
}
private:
//! False if the first constructor is called, true if the user passed a lambda.
//! False if the first constructor is called, true if the user passed a
//! lambda.
bool callbackUsed;
//! The number of epochs to wait before terminating the optimization process.
@@ -0,0 +1,61 @@
/**
* @file grad_clip_by_norm.hpp
* @author Marcus Edel
*
* Clip the gradients by multiplying the unit vector of the gradients with the
* threshold.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CALLBACKS_GRAD_CLIP_BY_NORM_HPP
#define ENSMALLEN_CALLBACKS_GRAD_CLIP_BY_NORM_HPP
namespace ens {
/**
* Clip the gradients by multiplying the unit vector of the gradients with the
* threshold.
*/
class GradClipByNorm
{
public:
/**
* Set up the gradient clip by norm callback class with the maximum clipping
* value.
*
* @param maxNorm The maximum clipping value.
*/
GradClipByNorm(const double maxNorm) : maxNorm(maxNorm)
{ /* Nothing to do here. */ }
/**
* Callback function called at any call to Gradient().
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param gradient Matrix that holds the gradient.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
bool Gradient(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
MatType& gradient)
{
const double gradientNorm = arma::norm(gradient);
if (gradientNorm > maxNorm)
gradient = maxNorm * gradient / gradientNorm;
return false;
}
private:
//! The maximum clipping value for gradient clipping.
const double maxNorm;
};
} // namespace ens
#endif
@@ -0,0 +1,61 @@
/**
* @file grad_clip_by_value.hpp
* @author Marcus Edel
*
* Clips the gradient to a specified min and max.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CALLBACKS_GRAD_CLIP_BY_VALUE_HPP
#define ENSMALLEN_CALLBACKS_GRAD_CLIP_BY_VALUE_HPP
namespace ens {
/**
* Clip the gradient to a specified min and max.
*/
class GradClipByValue
{
public:
/**
* Set up the gradient clip by value callback class with the min and max
* value.
*
* @param min The minimum value to clip to.
* @param max The maximum value to clip to.
*/
GradClipByValue(const double min, const double max) : lower(min), upper(max)
{ /* Nothing to do here. */ }
/**
* Callback function called at any call to Gradient().
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param gradient Matrix that holds the gradient.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
bool Gradient(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
MatType& gradient)
{
gradient = arma::clamp(gradient, lower, upper);
return false;
}
private:
//! The minimum value to clip to.
const double lower;
//! The maximum value to clip to.
const double upper;
};
} // namespace ens
#endif
@@ -38,13 +38,14 @@ class PrintLoss
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void EndEpoch(OptimizerType& /* optimizer */,
bool EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double objective)
{
output << objective << std::endl;
return false;
}
private:
@@ -98,7 +98,7 @@ class ProgressBar
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginEpoch(OptimizerType& /* optimizer */,
bool BeginEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t epochIn,
@@ -113,6 +113,8 @@ class ProgressBar
epoch = epochIn;
newEpoch = true;
return false;
}
/**
@@ -124,7 +126,7 @@ class ProgressBar
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void StepTaken(OptimizerType& /* optimizer */,
bool StepTaken(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */)
{
@@ -163,6 +165,8 @@ class ProgressBar
output.flush();
stepTimer.tic();
return false;
}
/**
@@ -174,13 +178,14 @@ class ProgressBar
* @param objectiveIn Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void Evaluate(OptimizerType& optimizer,
bool Evaluate(OptimizerType& optimizer,
FunctionType& /* function */,
const MatType& /* coordinates */,
const double objectiveIn)
{
objective += objectiveIn / optimizer.BatchSize();
steps++;
return false;
}
/**
@@ -193,7 +198,7 @@ class ProgressBar
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void EndEpoch(OptimizerType& /* optimizer */,
bool EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
@@ -216,11 +221,12 @@ class ProgressBar
output << ".";
}
}
const size_t stepTime = epochTimer.toc() / (double) epochSize * 1000;
output << "] " << progress << "% - " << (size_t) epochTimer.toc() % 60
<< "s " << stepTime << "ms/step " << "- loss: " << objective << "\n";
const double epochTimerElapsed = epochTimer.toc();
const size_t stepTime = epochTimerElapsed / (double) epochSize * 1000;
output << "] " << progress << "% - " << epochTimerElapsed
<< "s/epoch; " << stepTime << "ms/step; loss: " << objective << "\n";
output.flush();
return false;
}
private:
@@ -0,0 +1,89 @@
/**
* @file query_front.hpp
* @author Nanubala Gnana Sai
*
* Implementation of the query front callback function.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CALLBACKS_QUERY_FRONT_HPP
#define ENSMALLEN_CALLBACKS_QUERY_FRONT_HPP
namespace ens {
/**
* Query the current Pareto Front after every GenerationalStepTaken callback function.
*/
class QueryFront
{
public:
/**
* Set up the query front callback class with the specified inputs.
*
* @param queryRate The frequency at which the Pareto Front is queried.
* @param paretoFrontArray A reference to a vector of cube to store the
* queried fronts.
*/
QueryFront(const size_t queryRate,
std::vector<arma::cube>& paretoFrontArray) :
queryRate(queryRate),
paretoFrontArray(paretoFrontArray),
genCounter(0)
{ /* Nothing to do here */ }
/**
* Callback function called at the end of a single generational run.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objectives The set of calculated objectives so far.
* @param frontIndices The indices of the members belonging to Pareto Front.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename ObjectivesVecType,
typename IndicesType>
bool GenerationalStepTaken(OptimizerType& /* opt */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const ObjectivesVecType& objectives,
const IndicesType& frontIndices)
{
arma::cube currentParetoFront{};
if (genCounter % queryRate == 0)
{
currentParetoFront.resize(objectives[0].n_rows, objectives[0].n_cols,
frontIndices[0].size());
for (size_t solutionIdx = 0; solutionIdx < frontIndices[0].size();
++solutionIdx)
{
currentParetoFront.slice(solutionIdx) = arma::conv_to<arma::mat>::from(
objectives[frontIndices[0][solutionIdx]]);
}
paretoFrontArray.emplace_back(std::move(currentParetoFront));
}
++genCounter;
return false;
}
private:
//! The rate of query.
size_t queryRate;
//! A reference to the array of pareto fronts.
std::vector<arma::cube>& paretoFrontArray;
//! A counter for the current generation.
size_t genCounter;
};
} // namespace ens
#endif
+15 -7
View File
@@ -87,6 +87,7 @@ class Report
TruncatePrint(initialCoordinates, outputMatrixSize);
output << std::endl << "Final coordinates: " << std::endl;
TruncatePrint(coordinates, outputMatrixSize);
output << std::endl;
}
else
{
@@ -239,13 +240,14 @@ class Report
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginEpoch(OptimizerType& /* optimizer */,
bool BeginEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double /* objective */)
{
epochCalls++;
return false;
}
/**
@@ -258,7 +260,7 @@ class Report
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void EndEpoch(OptimizerType& optimizer,
bool EndEpoch(OptimizerType& optimizer,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
@@ -282,6 +284,7 @@ class Report
gradientsNorm.push_back(gradientNorm);
SaveStepSize(optimizer);
return false;
}
/**
@@ -293,7 +296,7 @@ class Report
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void StepTaken(OptimizerType& optimizer,
bool StepTaken(OptimizerType& optimizer,
FunctionType& /* function */,
const MatType& /* coordinates */)
{
@@ -307,6 +310,7 @@ class Report
SaveStepSize(optimizer);
}
return false;
}
/**
@@ -318,13 +322,14 @@ class Report
* @param objectiveIn Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void Evaluate(OptimizerType& /* optimizer */,
bool Evaluate(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const double objectiveIn)
{
objective = objectiveIn;
evaluateCalls++;
return false;
}
/**
@@ -337,7 +342,7 @@ class Report
* @param objectiveIn Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void EvaluateConstraint(OptimizerType& /* optimizer */,
bool EvaluateConstraint(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* constraint */,
@@ -345,6 +350,7 @@ class Report
{
objective += objectiveIn;
evaluateCalls++;
return false;
}
/**
@@ -356,7 +362,7 @@ class Report
* @param gradientIn Matrix that holds the gradient.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void Gradient(OptimizerType& /* optimizer */,
bool Gradient(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const MatType& gradientIn)
@@ -364,6 +370,7 @@ class Report
hasGradient = true;
gradientNorm = arma::norm(gradientIn);
gradientCalls++;
return false;
}
/**
@@ -376,13 +383,14 @@ class Report
* @param gradient Matrix that holds the gradient;
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void GradientConstraint(OptimizerType& optimizer,
bool GradientConstraint(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t /* constraint */,
const MatType& gradient)
{
Gradient(optimizer, function, coordinates, gradient);
return false;
}
private:
@@ -40,7 +40,7 @@ class StoreBestCoordinates
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void Evaluate(OptimizerType& /* optimizer */,
bool Evaluate(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& coordinates,
const double objective)
@@ -50,6 +50,7 @@ class StoreBestCoordinates
bestObjective = objective;
bestCoordinates = coordinates;
}
return false;
}
//! Get the best coordinates.
@@ -45,6 +45,30 @@ class TimerStop
timer.tic();
}
/**
* Callback function called when a step is taken.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param epoch The index of the current epoch.
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
bool EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */)
{
if (timer.toc() > duration)
{
Info << "Timer timeout (" << duration << "s) reached; terminating "
<< "optimization." << std::endl;
return true;
}
return false;
}
/**
* Callback function called at the end of a pass over the data.
*
@@ -63,7 +87,8 @@ class TimerStop
{
if (timer.toc() > duration)
{
Info << "Timer timeout reached; terminate optimization." << std::endl;
Info << "Timer timeout (" << duration << "s) reached; terminating "
<< "optimization." << std::endl;
return true;
}
+220 -47
View File
@@ -37,6 +37,8 @@ ENS_HAS_EXACT_METHOD_FORM(BeginEpoch, HasBeginEpoch)
ENS_HAS_EXACT_METHOD_FORM(EndEpoch, HasEndEpoch)
//! Detect an StepTaken() method.
ENS_HAS_EXACT_METHOD_FORM(StepTaken, HasStepTaken)
//! Detect an GenerationalStepTaken() method.
ENS_HAS_EXACT_METHOD_FORM(GenerationalStepTaken, HasGenerationalStepTaken)
template<typename OptimizerType,
typename FunctionType,
@@ -47,7 +49,7 @@ struct TypedForms
//! This is the form of a bool Evaluate() callback method.
template<typename CallbackType>
using EvaluateBoolForm =
void(CallbackType::*)(OptimizerType&,
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const double);
@@ -63,7 +65,7 @@ struct TypedForms
//! This is the form of a bool EvaluateConstraint() callback method.
template<typename CallbackType>
using EvaluateConstraintBoolForm =
void(CallbackType::*)(OptimizerType&,
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
@@ -81,11 +83,20 @@ struct TypedForms
//! This is the form of a bool Gradient() callback method.
template<typename CallbackType>
using GradientBoolForm =
void(CallbackType::*)(OptimizerType&,
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const MatType&);
//! This is the form of a bool Gradient() callback method where the gradient
//! is modifiable.
template<typename CallbackType>
using GradientBoolModifiableForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
MatType&);
//! This is the form of a void Gradient() callback method.
template<typename CallbackType>
using GradientVoidForm =
@@ -94,15 +105,34 @@ struct TypedForms
const MatType&,
const MatType&);
//! This is the form of a void Gradient() callback method where the gradient
//! is modifiable.
template<typename CallbackType>
using GradientVoidModifiableForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
MatType&);
//! This is the form of a bool GradientConstraint() callback method.
template<typename CallbackType>
using GradientConstraintBoolForm =
void(CallbackType::*)(OptimizerType&,
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const MatType&);
//! This is the form of a bool GradientConstraint() callback method where the
//! gradient is modifiable.
template<typename CallbackType>
using GradientConstraintBoolModifiableForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
MatType&);
//! This is the form of a void GradientConstraint() callback method.
template<typename CallbackType>
using GradientConstraintVoidForm =
@@ -112,6 +142,16 @@ struct TypedForms
const size_t,
const MatType&);
//! This is the form of a void GradientConstraint() callback method where the
//! gradient is modifiable.
template<typename CallbackType>
using GradientConstraintVoidModifiableForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
MatType&);
//! This is the form of a bool BeginOptimization() callback method.
template<typename CallbackType>
using BeginOptimizationBoolForm =
@@ -199,23 +239,11 @@ template<typename CallbackType,
typename MatType>
struct HasBeginOptimizationSignature
{
const static bool hasBool =
constexpr static bool value =
HasBeginOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginOptimizationBoolForm>::value ||
HasBeginOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginOptimizationBoolForm>::value &&
!HasBeginOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginOptimizationVoidForm>::value;
const static bool hasVoid =
!HasBeginOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginOptimizationBoolForm>::value &&
HasBeginOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginOptimizationVoidForm>::value;
const static bool hasNone =
!HasBeginOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginOptimizationBoolForm>::value &&
!HasBeginOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginOptimizationVoidForm>::value;
};
//! Utility struct, check if either void Evaluate() or bool Evaluate()
@@ -226,11 +254,23 @@ template<typename CallbackType,
typename MatType>
struct HasEvaluateSignature
{
const static bool value =
constexpr static bool hasBool =
HasEvaluate<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateBoolForm>::value ||
FunctionType, MatType>::template EvaluateBoolForm>::value &&
!HasEvaluate<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateVoidForm>::value;
constexpr static bool hasVoid =
!HasEvaluate<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateBoolForm>::value &&
HasEvaluate<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateVoidForm>::value;
FunctionType, MatType>::template EvaluateVoidForm>::value;
constexpr static bool hasNone =
!HasEvaluate<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateBoolForm>::value &&
!HasEvaluate<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateVoidForm>::value;
};
//! Utility struct, check if either void EvaluateConstraint() or
@@ -241,11 +281,23 @@ template<typename CallbackType,
typename MatType>
struct HasEvaluateConstraintSignature
{
const static bool value =
constexpr static bool hasBool =
HasEvaluateConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateConstraintBoolForm>::value ||
FunctionType, MatType>::template EvaluateConstraintBoolForm>::value &&
!HasEvaluateConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateConstraintVoidForm>::value;
constexpr static bool hasVoid =
!HasEvaluateConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateConstraintBoolForm>::value &&
HasEvaluateConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateConstraintVoidForm>::value;
FunctionType, MatType>::template EvaluateConstraintVoidForm>::value;
constexpr static bool hasNone =
!HasEvaluateConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateConstraintBoolForm>::value &&
!HasEvaluateConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EvaluateConstraintVoidForm>::value;
};
//! Utility struct, check if either void Gradient() or bool Gradient()
@@ -257,11 +309,41 @@ template<typename CallbackType,
typename Gradient>
struct HasGradientSignature
{
const static bool value =
constexpr static bool hasBool =
(HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientBoolForm>::value ||
HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientBoolForm>::value ||
FunctionType, MatType, Gradient>::template
GradientBoolModifiableForm>::value) &&
(!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientVoidForm>::value ||
!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientVoidModifiableForm>::value);
constexpr static bool hasVoid =
(!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientBoolForm>::value ||
!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientBoolModifiableForm>::value) &&
(HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientVoidForm>::value ||
HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientVoidForm>::value;
FunctionType, MatType, Gradient>::template
GradientVoidModifiableForm>::value);
constexpr static bool hasNone =
!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientBoolForm>::value &&
!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientBoolModifiableForm>::value &&
!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientVoidForm>::value &&
!HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientVoidModifiableForm>::value;
};
//! Utility struct, check if either void GradientConstraint() or
@@ -273,13 +355,29 @@ template<typename CallbackType,
typename Gradient>
struct HasGradientConstraintSignature
{
const static bool value =
constexpr static bool hasBool =
HasGradientConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType,
Gradient>::template GradientConstraintBoolForm>::value ||
FunctionType, MatType, Gradient>::template
GradientConstraintBoolForm>::value &&
!HasGradientConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientConstraintVoidForm>::value;
constexpr static bool hasVoid =
!HasGradientConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientConstraintBoolForm>::value &&
HasGradientConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType,
Gradient>::template GradientConstraintVoidForm>::value;
FunctionType, MatType, Gradient>::template
GradientConstraintVoidForm>::value;
constexpr static bool hasNone =
!HasGradientConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientConstraintBoolForm>::value &&
!HasGradientConstraint<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template
GradientConstraintVoidForm>::value;
};
//! Utility struct, check if either void EndOptimization() or
@@ -290,11 +388,11 @@ template<typename CallbackType,
typename MatType>
struct HasEndOptimizationSignature
{
const static bool value =
constexpr static bool value =
HasEndOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndOptimizationBoolForm>::value ||
FunctionType, MatType>::template EndOptimizationBoolForm>::value ||
HasEndOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndOptimizationVoidForm>::value;
FunctionType, MatType>::template EndOptimizationVoidForm>::value;
};
//! Utility struct, check if either void BeginEpoch() or bool BeginEpoch()
@@ -305,11 +403,23 @@ template<typename CallbackType,
typename MatType>
struct HasBeginEpochSignature
{
const static bool value =
constexpr static bool hasBool =
HasBeginEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginEpochBoolForm>::value ||
FunctionType, MatType>::template BeginEpochBoolForm>::value &&
!HasBeginEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginEpochVoidForm>::value;
constexpr static bool hasVoid =
!HasBeginEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginEpochBoolForm>::value &&
HasBeginEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginEpochVoidForm>::value;
FunctionType, MatType>::template BeginEpochVoidForm>::value;
constexpr static bool hasNone =
!HasBeginEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginEpochBoolForm>::value &&
!HasBeginEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template BeginEpochVoidForm>::value;
};
//! Utility struct, check if either void EndEpoch() or bool EndEpoch()
@@ -320,19 +430,19 @@ template<typename CallbackType,
typename MatType>
struct HasEndEpochSignature
{
const static bool hasBool =
constexpr static bool hasBool =
HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochBoolForm>::value &&
!HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochVoidForm>::value;
const static bool hasVoid =
constexpr static bool hasVoid =
!HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochBoolForm>::value &&
HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochVoidForm>::value;
const static bool hasNone =
constexpr static bool hasNone =
!HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochBoolForm>::value &&
!HasEndEpoch<CallbackType, TypedForms<OptimizerType,
@@ -346,25 +456,88 @@ template<typename CallbackType,
typename MatType>
struct HasStepTakenSignature
{
const static bool hasBool =
constexpr static bool hasBool =
HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenBoolForm>::value &&
!HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenVoidForm>::value;
const static bool hasVoid =
constexpr static bool hasVoid =
!HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenBoolForm>::value &&
HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenVoidForm>::value;
FunctionType, MatType>::template StepTakenVoidForm>::value;
const static bool hasNone =
constexpr static bool hasNone =
!HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenBoolForm>::value &&
!HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenVoidForm>::value;
FunctionType, MatType>::template StepTakenVoidForm>::value;
};
//! A utility struct for Typed Forms required in
//! callbacks for MultiObjective Optimizers.
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename ObjectivesVecType,
typename IndicesType,
typename GradType = MatType>
struct MOOTypedForms
{
//! This is the form of a bool GenerationalStepTaken() for MOO callback method.
template<typename CallbackType>
using GenerationalStepTakenBoolForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const ObjectivesVecType&,
const IndicesType&);
//! This is the form of a void StepTaken() for MOO callback method.
template<typename CallbackType>
using GenerationalStepTakenVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const ObjectivesVecType&,
const IndicesType&);
};
//! Utility struct, check if either void StepTaken() or bool StepTaken() exists.
//! Specialization for Multiobjective case.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename ObjectivesVecType,
typename IndicesType,
typename MatType>
struct HasGenerationalStepTakenSignature
{
constexpr static bool hasBool =
HasGenerationalStepTaken<CallbackType, MOOTypedForms<OptimizerType,
FunctionType, MatType, ObjectivesVecType, IndicesType>::
template GenerationalStepTakenBoolForm>::value &&
!HasGenerationalStepTaken<CallbackType, MOOTypedForms<OptimizerType,
FunctionType, MatType, ObjectivesVecType, IndicesType>::
template GenerationalStepTakenVoidForm>::value;
constexpr static bool hasVoid =
!HasGenerationalStepTaken<CallbackType, MOOTypedForms<OptimizerType,
FunctionType, MatType, ObjectivesVecType, IndicesType>::
template GenerationalStepTakenBoolForm>::value &&
HasGenerationalStepTaken<CallbackType, MOOTypedForms<OptimizerType,
FunctionType, MatType, ObjectivesVecType, IndicesType>::
template GenerationalStepTakenVoidForm>::value;
constexpr static bool hasNone =
!HasGenerationalStepTaken<CallbackType, MOOTypedForms<OptimizerType,
FunctionType, MatType, ObjectivesVecType, IndicesType>::
template GenerationalStepTakenBoolForm>::value &&
!HasGenerationalStepTaken<CallbackType, MOOTypedForms<OptimizerType,
FunctionType, MatType, ObjectivesVecType, IndicesType>::
template GenerationalStepTakenVoidForm>::value;
};
} // namespace traits
} // namespace callbacks
} // namespace ens
@@ -1,16 +1,16 @@
/**
* @file scd.hpp
* @file cd.hpp
* @author Shikhar Bhardwaj
*
* Stochastic Coordinate Descent (SCD).
* Coordinate Descent (CD).
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_SCD_SCD_HPP
#define ENSMALLEN_SCD_SCD_HPP
#ifndef ENSMALLEN_CD_CD_HPP
#define ENSMALLEN_CD_CD_HPP
#include "descent_policies/cyclic_descent.hpp"
#include "descent_policies/random_descent.hpp"
@@ -42,7 +42,7 @@ namespace ens {
* }
* @endcode
*
* SCD can optimize partially differentiable functions. For more details, see
* CD can optimize partially differentiable functions. For more details, see
* the documentation on function types included with this distribution or on the
* ensmallen website.
*
@@ -50,11 +50,11 @@ namespace ens {
* coordinate for descent is selected.
*/
template <typename DescentPolicyType = RandomDescent>
class SCD
class CD
{
public:
/**
* Construct the SCD optimizer with the given function and parameters. The
* Construct the CD optimizer with the given function and parameters. The
* default value here are not necessarily good for every problem, so it is
* suggested that the values used are tailored for the task at hand. The
* maximum number of iterations refers to the maximum number of "descents"
@@ -70,11 +70,11 @@ class SCD
* @param descentPolicy The policy to use for picking up the coordinate to
* descend on.
*/
SCD(const double stepSize = 0.01,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const size_t updateInterval = 1e3,
const DescentPolicyType descentPolicy = DescentPolicyType());
CD(const double stepSize = 0.01,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const size_t updateInterval = 1e3,
const DescentPolicyType descentPolicy = DescentPolicyType());
/**
* Optimize the given function using stochastic coordinate descent. The
@@ -94,7 +94,7 @@ class SCD
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(ResolvableFunctionType& function,
MatType& iterate,
@@ -158,6 +158,24 @@ class SCD
} // namespace ens
// Include implementation.
#include "scd_impl.hpp"
#include "cd_impl.hpp"
namespace ens {
/**
* Backwards-compatibility alias; this can be removed after ensmallen 3.10.0.
* The history here is that CD was originally named SCD, but that is an
* inaccurate name because this is not a stochastic technique; thus, it was
* renamed SCD.
*/
template<typename DescentPolicyType = RandomDescent>
using SCD = CD<DescentPolicyType>;
// Convenience typedefs.
using RandomCD = CD<RandomDescent>;
using GreedyCD = CD<GreedyDescent>;
using CyclicCD = CD<CyclicDescent>;
} // namespace ens
#endif
@@ -1,26 +1,26 @@
/**
* @file scd_impl.hpp
* @file cd_impl.hpp
* @author Shikhar Bhardwaj
*
* Implementation of stochastic coordinate descent.
* Implementation of coordinate descent.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_SCD_SCD_IMPL_HPP
#define ENSMALLEN_SCD_SCD_IMPL_HPP
#ifndef ENSMALLEN_CD_CD_IMPL_HPP
#define ENSMALLEN_CD_CD_IMPL_HPP
// In case it hasn't been included yet.
#include "scd.hpp"
#include "cd.hpp"
#include <ensmallen_bits/function.hpp>
namespace ens {
template <typename DescentPolicyType>
SCD<DescentPolicyType>::SCD(
CD<DescentPolicyType>::CD(
const double stepSize,
const size_t maxIterations,
const double tolerance,
@@ -39,9 +39,9 @@ template <typename ResolvableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
SCD<DescentPolicyType>::Optimize(
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
CD<DescentPolicyType>::Optimize(
ResolvableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
@@ -66,10 +66,12 @@ SCD<DescentPolicyType>::Optimize(
// Controls early termination of the optimization process.
bool terminate = false;
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
// Start iterating.
terminate |= Callback::BeginOptimization(*this, function, iterate,
callbacks...);
for (size_t i = 1; i != maxIterations && !terminate; ++i)
Callback::BeginOptimization(*this, function, iterate, callbacks...);
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
{
// Get the coordinate to descend on.
size_t featureIdx = descentPolicy.template DescentFeature<
@@ -81,9 +83,11 @@ SCD<DescentPolicyType>::Optimize(
terminate |= Callback::Gradient(*this, function, iterate, overallObjective,
gradient, callbacks...);
if (terminate)
break;
// Update the decision variable with the partial gradient.
iterate.col(featureIdx) -= stepSize * gradient.col(featureIdx);
iterate.col(featureIdx) -= ElemType(stepSize) * gradient.col(featureIdx);
terminate |= Callback::StepTaken(*this, function, iterate, callbacks...);
// Check for convergence.
@@ -94,12 +98,12 @@ SCD<DescentPolicyType>::Optimize(
overallObjective, callbacks...);
// Output current objective function.
Info << "SCD: iteration " << i << ", objective " << overallObjective
Info << "CD: iteration " << i << ", objective " << overallObjective
<< "." << std::endl;
if (std::isnan(overallObjective) || std::isinf(overallObjective))
{
Warn << "SCD: converged to " << overallObjective << "; terminating"
Warn << "CD: converged to " << overallObjective << "; terminating"
<< " with failure. Try a smaller step size?" << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
@@ -108,7 +112,7 @@ SCD<DescentPolicyType>::Optimize(
if (std::abs(lastObjective - overallObjective) < tolerance)
{
Info << "SCD: minimized within tolerance " << tolerance << "; "
Info << "CD: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
@@ -119,12 +123,16 @@ SCD<DescentPolicyType>::Optimize(
}
}
Info << "SCD: maximum iterations (" << maxIterations << ") reached; "
<< "terminating optimization." << std::endl;
// Calculate and return final objective.
if (!terminate)
{
Info << "CD: maximum iterations (" << maxIterations << ") reached; "
<< "terminating optimization." << std::endl;
}
// Calculate and return final objective. No need to pay attention to the
// result of the callback.
const ElemType objective = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, objective, callbacks...);
(void) Callback::Evaluate(*this, function, iterate, objective, callbacks...);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return objective;
@@ -52,8 +52,11 @@ class RandomDescent
const MatType& /* iterate */,
const ResolvableFunctionType& function)
{
// return randi<size_t>(
// arma::distr_param(0, function.NumFeatures() - 1));
return arma::as_scalar(arma::randi<arma::uvec>(
1, arma::distr_param(0, function.NumFeatures() - 1)));
1, arma::distr_param(0, function.NumFeatures() - 1)));
}
};
@@ -0,0 +1,190 @@
/**
* @file active_cmaes.hpp
* @author Marcus Edel
* @author Suvarsha Chennareddy
*
* Definition of the Active Covariance Matrix Adaptation Evolution Strategy
* as proposed by G.A Jastrebski and D.V Arnold in "Improving Evolution
* Strategies through Active Covariance Matrix Adaptation".
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CMAES_ACTIVE_CMAES_HPP
#define ENSMALLEN_CMAES_ACTIVE_CMAES_HPP
#include "full_selection.hpp"
#include "random_selection.hpp"
#include "transformation_policies/empty_transformation.hpp"
#include "transformation_policies/boundary_box_constraint.hpp"
namespace ens {
/**
* Active CMA-ES is a variant of the stochastic search algorithm
* CMA-ES - Covariance Matrix Adaptation Evolution Strategy.
* Active CMA-ES actively reduces the uncertainty in unfavourable directions by
* exploiting the information about bad mutations in the covariance matrix
* update step. This isn't for the purpose of accelerating progress, but
* instead for speeding up the adaptation of the covariance matrix (which, in
* turn, will lead to faster progress).
*
* For more information, please refer to:
*
* @code
* @INPROCEEDINGS{1688662,
* author = {Jastrebski, G.A. and Arnold, D.V.},
* booktitle = {2006 IEEE International Conference on Evolutionary
* Computation},
* title = {Improving Evolution Strategies through Active Covariance
* Matrix Adaptation},
* year = {2006},
* volume = {},
* number = {},
* pages = {2814-2821},
* doi = {10.1109/CEC.2006.1688662}}
* @endcode
*
* Active CMA-ES can optimize separable functions. For more details, see the
* documentation on function types included with this distribution or on the
* ensmallen website.
*
* @tparam SelectionPolicy The selection strategy used for the evaluation step.
* @tparam TransformationPolicy The transformation strategy used to
* map decision variables to the desired domain during fitness evaluation
* and termination. Use EmptyTransformation if the domain isn't bounded.
*/
template<typename SelectionPolicyType = FullSelection,
typename TransformationPolicyType = EmptyTransformation<>>
class ActiveCMAES
{
public:
/**
* Construct the Active CMA-ES optimizer with the given function and
* parameters. The defaults here are not necessarily good for the given
* problem, so it is suggested that the values used be tailored to the task at
* hand. The maximum number of iterations refers to the maximum number of
* points that are processed (i.e., one iteration equals one point; one
* iteration does not equal one pass over the dataset).
*
* @param lambda The population size (0 use the default size).
* @param transformationPolicy Instantiated transformation policy used to
* map the coordinates to the desired domain.
* @param batchSize Batch size to use for the objective calculation.
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param selectionPolicy Instantiated selection policy used to calculate the
* objective.
* @param stepSize Starting sigma/step size (will be modified).
*/
ActiveCMAES(
const size_t lambda = 0,
const TransformationPolicyType&
transformationPolicy = TransformationPolicyType(),
const size_t batchSize = 32,
const size_t maxIterations = 1000,
const double tolerance = 1e-5,
const SelectionPolicyType& selectionPolicy = SelectionPolicyType(),
double stepSize = 0);
/**
* Optimize the given function using Active CMA-ES. The given starting point
* will be modified to store the finishing point of the algorithm, and the
* final objective value is returned.
*
* @tparam SeparableFunctionType Type of the function to be optimized.
* @tparam MatType Type of matrix to optimize.
* @tparam CallbackTypes Types of callback functions.
* @param function Function to optimize.
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(
SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Get the population size.
size_t PopulationSize() const { return lambda; }
//! Modify the population size.
size_t& PopulationSize() { return lambda; }
//! Get the batch size.
size_t BatchSize() const { return batchSize; }
//! Modify the batch size.
size_t& BatchSize() { return batchSize; }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return maxIterations; }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return maxIterations; }
//! Get the tolerance for termination.
double Tolerance() const { return tolerance; }
//! Modify the tolerance for termination.
double& Tolerance() { return tolerance; }
//! Get the selection policy.
const SelectionPolicyType& SelectionPolicy() const { return selectionPolicy; }
//! Modify the selection policy.
SelectionPolicyType& SelectionPolicy() { return selectionPolicy; }
//! Get the transformation policy.
const TransformationPolicyType& TransformationPolicy() const
{ return transformationPolicy; }
//! Modify the transformation policy.
TransformationPolicyType& TransformationPolicy()
{ return transformationPolicy; }
//! Get the step size.
double StepSize() const
{ return stepSize; }
//! Modify the step size.
double& StepSize()
{ return stepSize; }
private:
//! Population size.
size_t lambda;
//! The batch size for processing.
size_t batchSize;
//! The maximum number of allowed iterations.
size_t maxIterations;
//! The tolerance for termination.
double tolerance;
//! The selection policy used to calculate the objective.
SelectionPolicyType selectionPolicy;
//! The transformationPolicy used to map coordinates to the suitable domain
//! while evaluating fitness. This mapping is also done after optimization
//! has completed.
TransformationPolicyType transformationPolicy;
//! The step size.
double stepSize;
};
/**
* Convenient typedef for Active CMAES approximation.
*/
template<typename TransformationPolicyType = EmptyTransformation<>,
typename SelectionPolicyType = RandomSelection>
using ApproxActiveCMAES = ActiveCMAES<SelectionPolicyType, TransformationPolicyType>;
} // namespace ens
// Include implementation.
#include "active_cmaes_impl.hpp"
#endif
@@ -0,0 +1,354 @@
/**
* @file active_cmaes_impl.hpp
* @author Marcus Edel
* @author Suvarsha Chennareddy
*
* Implementation of the Active Covariance Matrix Adaptation Evolution Strategy
* as proposed by G.A Jastrebski and D.V Arnold in "Improving Evolution
* Strategies through Active Covariance Matrix Adaptation".
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CMAES_ACTIVE_CMAES_IMPL_HPP
#define ENSMALLEN_CMAES_ACTIVE_CMAES_IMPL_HPP
// In case it hasn't been included yet.
#include "active_cmaes.hpp"
#include <ensmallen_bits/function.hpp>
namespace ens {
template<typename SelectionPolicyType, typename TransformationPolicyType>
ActiveCMAES<SelectionPolicyType, TransformationPolicyType>::ActiveCMAES(
const size_t lambda,
const TransformationPolicyType&
transformationPolicy,
const size_t batchSize,
const size_t maxIterations,
const double tolerance,
const SelectionPolicyType& selectionPolicy,
double stepSizeIn) :
lambda(lambda),
batchSize(batchSize),
maxIterations(maxIterations),
tolerance(tolerance),
selectionPolicy(selectionPolicy),
transformationPolicy(transformationPolicy),
stepSize(stepSizeIn)
{ /* Nothing to do. */ }
//! Optimize the function (minimize).
template<typename SelectionPolicyType, typename TransformationPolicyType>
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type ActiveCMAES<SelectionPolicyType,
TransformationPolicyType>::Optimize(
SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename ForwardType<MatType>::bcol BaseColType;
typedef typename ForwardType<MatType>::uvec UVecType;
// Make sure that we have the methods that we need. Long name...
traits::CheckArbitrarySeparableFunctionTypeAPI<
SeparableFunctionType, BaseMatType>();
RequireDenseFloatingPointType<BaseMatType>();
BaseMatType& iterate = (BaseMatType&) iterateIn;
// Find the number of functions to use.
const size_t numFunctions = function.NumFunctions();
// Population size.
if (lambda == 0)
lambda = (4 + std::round(3 * std::log(iterate.n_elem))) * 10;
// Parent number.
const size_t mu = std::round(lambda / 4);
// Recombination weight (w = 1 / (parent number)).
const ElemType w = 1.0 / mu;
// Number of effective solutions.
const ElemType muEffective = mu;
// Step size control parameters.
BaseMatType sigma(2, 1); // sigma is vector-shaped.
if (stepSize == 0)
sigma(0) = transformationPolicy.InitialStepSize();
else
sigma(0) = ElemType(stepSize);
const ElemType cs = 4 / ElemType(iterate.n_elem + 4);
const ElemType ds = 1 + cs;
const ElemType enn = std::sqrt(iterate.n_elem) * (1 -
1 / ElemType(4 * iterate.n_elem) +
1 / ElemType(21 * std::pow(iterate.n_elem, 2)));
// Covariance update parameters. Cumulation for distribution.
const ElemType cc = cs;
const ElemType ccov = 2 /
std::pow((iterate.n_elem + std::sqrt(ElemType(2))), ElemType(2));
const ElemType beta = (4 * mu - 2) /
(std::pow(ElemType(iterate.n_elem + 12), ElemType(2)) + 4 * mu);
std::vector<BaseMatType> mPosition(2, BaseMatType(iterate.n_rows,
iterate.n_cols));
mPosition[0] = iterate;
BaseMatType step(iterate.n_rows, iterate.n_cols);
step.zeros();
BaseMatType transformedIterate = transformationPolicy.Transform(iterate);
// Controls early termination of the optimization process.
bool terminate = false;
// Calculate the first objective function.
ElemType currentObjective = 0;
for (size_t f = 0; f < numFunctions; f += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - f);
const ElemType objective = function.Evaluate(transformedIterate, f,
effectiveBatchSize);
currentObjective += objective;
terminate |= Callback::Evaluate(*this, function, transformedIterate,
objective, callbacks...);
}
ElemType overallObjective = currentObjective;
ElemType lastObjective = std::numeric_limits<ElemType>::max();
// Population parameters.
std::vector<BaseMatType> pStep(lambda, BaseMatType(iterate.n_rows,
iterate.n_cols));
std::vector<BaseMatType> pPosition(lambda, BaseMatType(iterate.n_rows,
iterate.n_cols));
BaseMatType pObjective(lambda, 1); // pObjective is vector-shaped.
std::vector<BaseMatType> ps(2, BaseMatType(iterate.n_rows, iterate.n_cols));
ps[0].zeros();
ps[1].zeros();
std::vector<BaseMatType> pc = ps;
std::vector<BaseMatType> C(2, BaseMatType(iterate.n_elem, iterate.n_elem));
C[0].eye();
// Covariance matrix parameters.
BaseColType eigval;
BaseMatType eigvec;
BaseMatType eigvalZero(iterate.n_elem, 1); // eigvalZero is vector-shaped.
eigvalZero.zeros();
// The current visitation order (sorted by population objectives).
UVecType idx = linspace<UVecType>(0, lambda - 1, lambda);
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
// Now iterate!
Callback::BeginOptimization(*this, function, transformedIterate,
callbacks...);
size_t idx0, idx1;
// The number of generations to wait after the minimum loss has
// been reached or no improvement has been made before terminating.
size_t patience = 10 + (30 * iterate.n_elem / lambda) + 1;
size_t steps = 0;
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
{
// To keep track of where we are.
idx0 = i % 2;
idx1 = (i + 1) % 2;
// Perform Cholesky decomposition. If the matrix is not positive definite,
// add a small value and try again.
BaseMatType covLower;
// while (!arma::chol(covLower, C[idx0], "lower"))
while (!chol(covLower, C[idx0]))
C[idx0].diag() += std::numeric_limits<ElemType>::epsilon();
eig_sym(eigval, eigvec, C[idx0]);
for (size_t j = 0; j < lambda; ++j)
{
if (iterate.n_rows > iterate.n_cols)
{
pStep[idx(j)] = covLower *
randn<BaseMatType>(iterate.n_rows, iterate.n_cols);
}
else
{
pStep[idx(j)] = randn<BaseMatType>(iterate.n_rows, iterate.n_cols)
* covLower.t();
}
pPosition[idx(j)] = mPosition[idx0] + sigma(idx0) * pStep[idx(j)];
// Calculate the objective function.
pObjective(idx(j)) = selectionPolicy.Select(function, batchSize,
transformationPolicy.Transform(pPosition[idx(j)]), terminate,
callbacks...);
}
// Sort population.
idx = sort_index(pObjective);
step = w * pStep[idx(0)];
for (size_t j = 1; j < mu; ++j)
step += w * pStep[idx(j)];
mPosition[idx1] = mPosition[idx0] + sigma(idx0) * step;
// Calculate the objective function.
currentObjective = selectionPolicy.Select(function, batchSize,
transformationPolicy.Transform(mPosition[idx1]), terminate,
callbacks...);
// Update best parameters.
if (currentObjective < overallObjective)
{
overallObjective = currentObjective;
iterate = mPosition[idx1];
transformedIterate = transformationPolicy.Transform(iterate);
terminate |= Callback::StepTaken(*this, function,
transformedIterate, callbacks...);
}
// Update Step Size.
if (iterate.n_rows > iterate.n_cols)
{
ps[idx1] = (1 - cs) * ps[idx0] + std::sqrt(
cs * (2 - cs) * muEffective) *
eigvec * diagmat(1 / eigval) * eigvec.t() * step;
}
else
{
ps[idx1] = (1 - cs) * ps[idx0] + std::sqrt(
cs * (2 - cs) * muEffective) * step *
eigvec * diagmat(1 / eigval) * eigvec.t();
}
const ElemType psNorm = norm(ps[idx1]);
sigma(idx1) = sigma(idx0) * std::exp(cs / ds * (psNorm / enn - 1));
if (std::isnan(sigma(idx1)) || sigma(idx1) > 1e14)
{
Warn << "The step size diverged to " << sigma(idx1) << "; "
<< "terminating with failure. Try a smaller step size?" << std::endl;
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
pc[idx1] = (1 - cc) * pc[idx0] + std::sqrt(cc * (2 - cc) *
muEffective) * step;
if (iterate.n_rows > iterate.n_cols)
{
C[idx1] = (1 - ccov) * C[idx0] + ccov *
(pc[idx1] * pc[idx1].t());
for (size_t j = 0; j < mu; ++j)
{
C[idx1] = C[idx1] + beta * w *
pStep[idx(j)] * pStep[idx(j)].t();
}
for (size_t j = lambda - mu; j < lambda; ++j)
{
C[idx1] = C[idx1] - beta * w *
pStep[idx(j)] * pStep[idx(j)].t();
}
}
else
{
C[idx1] = (1 - ccov) * C[idx0] + ccov *
(pc[idx1].t() * pc[idx1]);
for (size_t j = 0; j < mu; ++j)
{
C[idx1] = C[idx1] + beta * w *
pStep[idx(j)].t() * pStep[idx(j)];
}
for (size_t j = lambda - mu; j < lambda; ++j)
{
C[idx1] = C[idx1] - beta * w *
pStep[idx(j)].t() * pStep[idx(j)];
}
}
eig_sym(eigval, eigvec, C[idx1]);
const UVecType negativeEigval = find(eigval < 0, 1);
if (!negativeEigval.is_empty())
{
if (negativeEigval(0) == 0)
{
C[idx1].zeros();
}
else
{
C[idx1] = eigvec.cols(0, negativeEigval(0) - 1) *
diagmat(eigval.subvec(0, negativeEigval(0) - 1)) *
eigvec.cols(0, negativeEigval(0) - 1).t();
}
}
// Output current objective function.
Info << "Active CMA-ES: iteration " << i << ", objective " << overallObjective
<< "." << std::endl;
if (std::isnan(overallObjective) || std::isinf(overallObjective))
{
Warn << "Active CMA-ES: converged to " << overallObjective << "; "
<< "terminating with failure. Try a smaller step size?" << std::endl;
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
if (std::abs(lastObjective - overallObjective) < tolerance)
{
if (steps > patience)
{
Info << "Active CMA-ES: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
}
else
{
steps = 0;
}
steps++;
lastObjective = overallObjective;
}
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
} // namespace ens
#endif
+50 -31
View File
@@ -17,6 +17,8 @@
#include "full_selection.hpp"
#include "random_selection.hpp"
#include "transformation_policies/empty_transformation.hpp"
#include "transformation_policies/boundary_box_constraint.hpp"
namespace ens {
@@ -46,8 +48,12 @@ namespace ens {
* ensmallen website.
*
* @tparam SelectionPolicy The selection strategy used for the evaluation step.
* @tparam TransformationPolicy The transformation strategy used to
* map decision variables to the desired domain during fitness evaluation
* and termination. Use EmptyTransformation if the domain isn't bounded.
*/
template<typename SelectionPolicyType = FullSelection>
template<typename SelectionPolicyType = FullSelection,
typename TransformationPolicyType = EmptyTransformation<>>
class CMAES
{
public:
@@ -60,22 +66,24 @@ class CMAES
* equal one pass over the dataset).
*
* @param lambda The population size (0 use the default size).
* @param lowerBound Lower bound of decision variables.
* @param upperBound Upper bound of decision variables.
* @param transformationPolicy Instantiated transformation policy used to
* map the coordinates to the desired domain.
* @param batchSize Batch size to use for the objective calculation.
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param selectionPolicy Instantiated selection policy used to calculate the
* objective.
* @param stepSize Starting sigma/step size (will be modified).
*/
CMAES(const size_t lambda = 0,
const double lowerBound = -10,
const double upperBound = 10,
const TransformationPolicyType&
transformationPolicy = TransformationPolicyType(),
const size_t batchSize = 32,
const size_t maxIterations = 1000,
const double tolerance = 1e-5,
const SelectionPolicyType& selectionPolicy = SelectionPolicyType());
const SelectionPolicyType& selectionPolicy = SelectionPolicyType(),
double stepSize = 0);
/**
* Optimize the given function using CMA-ES. The given starting point will be
@@ -91,27 +99,17 @@ class CMAES
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Get the step size.
//! Get the population size.
size_t PopulationSize() const { return lambda; }
//! Modify the step size.
//! Modify the population size.
size_t& PopulationSize() { return lambda; }
//! Get the lower bound of decision variables.
double LowerBound() const { return lowerBound; }
//! Modify the lower bound of decision variables.
double& LowerBound() { return lowerBound; }
//! Get the upper bound of decision variables
double UpperBound() const { return upperBound; }
//! Modify the upper bound of decision variables
double& UpperBound() { return upperBound; }
//! Get the batch size.
size_t BatchSize() const { return batchSize; }
//! Modify the batch size.
@@ -132,16 +130,25 @@ class CMAES
//! Modify the selection policy.
SelectionPolicyType& SelectionPolicy() { return selectionPolicy; }
//! Get the transformation policy.
const TransformationPolicyType& TransformationPolicy() const
{ return transformationPolicy; }
//! Modify the transformation policy.
TransformationPolicyType& TransformationPolicy()
{ return transformationPolicy; }
//! Get the step size.
double StepSize() const { return stepSize; }
//! Modify the step size.
double& StepSize() { return stepSize; }
//! Get the total number of function evaluations.
size_t FunctionEvaluations() const { return functionEvaluations; }
private:
//! Population size.
size_t lambda;
//! Lower bound of decision variables.
double lowerBound;
//! Upper bound of decision variables
double upperBound;
//! The batch size for processing.
size_t batchSize;
@@ -153,13 +160,25 @@ class CMAES
//! The selection policy used to calculate the objective.
SelectionPolicyType selectionPolicy;
//! The transformationPolicy used to map coordinates to the suitable domain
//! while evaluating fitness. This mapping is also done after optimization
//! has completed.
TransformationPolicyType transformationPolicy;
//! The step size.
double stepSize;
//! Counter for the number of function evaluations.
size_t functionEvaluations = 0;
};
/**
* Convenient typedef for CMAES approximation.
*/
template<typename SelectionPolicyType = RandomSelection>
using ApproxCMAES = CMAES<SelectionPolicyType>;
template<typename TransformationPolicyType = EmptyTransformation<>,
typename SelectionPolicyType = RandomSelection>
using ApproxCMAES = CMAES<SelectionPolicyType, TransformationPolicyType>;
} // namespace ens
+106 -58
View File
@@ -22,36 +22,41 @@
namespace ens {
template<typename SelectionPolicyType>
CMAES<SelectionPolicyType>::CMAES(const size_t lambda,
const double lowerBound,
const double upperBound,
template<typename SelectionPolicyType, typename TransformationPolicyType>
CMAES<SelectionPolicyType, TransformationPolicyType>::CMAES(const size_t lambda,
const TransformationPolicyType&
transformationPolicy,
const size_t batchSize,
const size_t maxIterations,
const double tolerance,
const SelectionPolicyType& selectionPolicy) :
const SelectionPolicyType& selectionPolicy,
double stepSizeIn) :
lambda(lambda),
lowerBound(lowerBound),
upperBound(upperBound),
batchSize(batchSize),
maxIterations(maxIterations),
tolerance(tolerance),
selectionPolicy(selectionPolicy)
selectionPolicy(selectionPolicy),
transformationPolicy(transformationPolicy),
stepSize(stepSizeIn)
{ /* Nothing to do. */ }
//! Optimize the function (minimize).
template<typename SelectionPolicyType>
template<typename SelectionPolicyType, typename TransformationPolicyType>
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
typename MatType::elem_type CMAES<SelectionPolicyType,
TransformationPolicyType>::Optimize(
SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename ForwardType<MatType>::bcol bcol;
typedef typename ForwardType<MatType>::uvec UVecType;
typedef typename ForwardType<MatType>::bmat BaseMatType;
// Make sure that we have the methods that we need. Long name...
traits::CheckArbitrarySeparableFunctionTypeAPI<
@@ -69,16 +74,20 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
// Parent weights.
const size_t mu = std::round(lambda / 2);
BaseMatType w = std::log(mu + 0.5) - arma::log(
arma::linspace<BaseMatType>(0, mu - 1, mu) + 1.0);
w /= arma::accu(w);
BaseMatType w = std::log(mu + 0.5) - log(
linspace<BaseMatType>(0, mu - 1, mu) + 1.0);
w /= accu(w);
// Number of effective solutions.
const double muEffective = 1 / arma::accu(arma::pow(w, 2));
const double muEffective = 1 / accu(pow(w, 2));
// Step size control parameters.
BaseMatType sigma(2, 1); // sigma is vector-shaped.
sigma(0) = 0.3 * (upperBound - lowerBound);
if (stepSize == 0)
sigma(0) = transformationPolicy.InitialStepSize();
else
sigma(0) = stepSize;
const double cs = (muEffective + 2) / (iterate.n_elem + muEffective + 5);
const double ds = 1 + cs + 2 * std::max(std::sqrt((muEffective - 1) /
(iterate.n_elem + 1)) - 1, 0.0);
@@ -99,23 +108,27 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
std::vector<BaseMatType> mPosition(2, BaseMatType(iterate.n_rows,
iterate.n_cols));
mPosition[0] = lowerBound + arma::randu<BaseMatType>(
iterate.n_rows, iterate.n_cols) * (upperBound - lowerBound);
mPosition[0] = iterate;
BaseMatType step(iterate.n_rows, iterate.n_cols);
step.zeros();
BaseMatType transformedIterate = transformationPolicy.Transform(iterate);
// Controls early termination of the optimization process.
bool terminate = false;
// Calculate the first objective function.
ElemType currentObjective = 0;
for (size_t f = 0; f < numFunctions; f += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - f);
const ElemType objective = function.Evaluate(mPosition[0], f,
const ElemType objective = function.Evaluate(transformedIterate, f,
effectiveBatchSize);
currentObjective += objective;
Callback::Evaluate(*this, function, mPosition[0], objective,
callbacks...);
terminate |= Callback::Evaluate(*this, function, transformedIterate,
objective, callbacks...);
}
ElemType overallObjective = currentObjective;
@@ -135,54 +148,65 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
C[0].eye();
// Covariance matrix parameters.
arma::Col<ElemType> eigval; // TODO: might need a more general type.
bcol eigval; // TODO: might need a more general type.
BaseMatType eigvec;
BaseMatType eigvalZero(iterate.n_elem, 1); // eigvalZero is vector-shaped.
eigvalZero.zeros();
// The current visitation order (sorted by population objectives).
arma::uvec idx = arma::linspace<arma::uvec>(0, lambda - 1, lambda);
UVecType idx = linspace<UVecType>(0, lambda - 1, lambda);
// Controls early termination of the optimization process.
bool terminate = false;
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
// Now iterate!
terminate |= Callback::BeginOptimization(*this, function, iterate,
Callback::BeginOptimization(*this, function, transformedIterate,
callbacks...);
for (size_t i = 1; i < maxIterations && !terminate; ++i)
// The number of generations to wait after the minimum loss has
// been reached or no improvement has been made before terminating.
size_t patience = 10 + (30 * iterate.n_elem / lambda) + 1;
size_t steps = 0;
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
{
// To keep track of where we are.
const size_t idx0 = (i - 1) % 2;
const size_t idx1 = i % 2;
const size_t idx0 = i % 2;
const size_t idx1 = (i + 1) % 2;
// Perform Cholesky decomposition. If the matrix is not positive definite,
// add a small value and try again.
BaseMatType covLower;
while (!arma::chol(covLower, C[idx0], "lower"))
C[idx0].diag() += 1e-16;
// while (!chol(covLower, C[idx0], "lower"))
while (!chol(covLower, C[idx0]))
C[idx0].diag() += std::numeric_limits<ElemType>::epsilon();
eig_sym(eigval, eigvec, C[idx0]);
for (size_t j = 0; j < lambda; ++j)
{
if (iterate.n_rows > iterate.n_cols)
{
pStep[idx(j)] = covLower *
arma::randn<BaseMatType>(iterate.n_rows, iterate.n_cols);
pStep[idx(j)] = covLower * BaseMatType(
iterate.n_rows, iterate.n_cols, GetFillType<MatType>::randn);
}
else
{
pStep[idx(j)] = arma::randn<BaseMatType>(iterate.n_rows, iterate.n_cols)
* covLower;
pStep[idx(j)] = BaseMatType(
iterate.n_rows, iterate.n_cols, GetFillType<MatType>::randn) *
covLower.t();
}
pPosition[idx(j)] = mPosition[idx0] + sigma(idx0) * pStep[idx(j)];
// Calculate the objective function.
pObjective(idx(j)) = selectionPolicy.Select(function, batchSize,
pPosition[idx(j)], callbacks...);
transformationPolicy.Transform(pPosition[idx(j)]), terminate,
callbacks...);
}
// Sort population.
idx = arma::sort_index(pObjective);
idx = sort_index(pObjective);
step = w(0) * pStep[idx(0)];
for (size_t j = 1; j < mu; ++j)
@@ -192,7 +216,8 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
// Calculate the objective function.
currentObjective = selectionPolicy.Select(function, batchSize,
mPosition[idx1], callbacks...);
transformationPolicy.Transform(mPosition[idx1]), terminate,
callbacks...);
// Update best parameters.
if (currentObjective < overallObjective)
@@ -200,39 +225,54 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
overallObjective = currentObjective;
iterate = mPosition[idx1];
terminate |= Callback::StepTaken(*this, function, iterate, callbacks...);
transformedIterate = transformationPolicy.Transform(iterate);
terminate |= Callback::StepTaken(*this, function,
transformedIterate, callbacks...);
}
// Update Step Size.
if (iterate.n_rows > iterate.n_cols)
{
ps[idx1] = (1 - cs) * ps[idx0] + std::sqrt(
cs * (2 - cs) * muEffective) * covLower.t() * step;
cs * (2 - cs) * muEffective) * eigvec *
diagmat(1 / eigval) * eigvec.t() * step;
}
else
{
ps[idx1] = (1 - cs) * ps[idx0] + std::sqrt(
cs * (2 - cs) * muEffective) * step * covLower.t();
cs * (2 - cs) * muEffective) * step * eigvec *
diagmat(1 / eigval) * eigvec.t();
}
const ElemType psNorm = arma::norm(ps[idx1]);
sigma(idx1) = sigma(idx0) * std::exp(cs / ds * ( psNorm / enn - 1));
const ElemType psNorm = norm(ps[idx1]);
sigma(idx1) = sigma(idx0) * std::exp(cs / ds * (psNorm / enn - 1));
if (std::isnan(sigma(idx1)) || sigma(idx1) > 1e14)
{
Warn << "The step size diverged to " << sigma(idx1) << "; "
<< "terminating with failure. Try a smaller step size?" << std::endl;
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
// Update covariance matrix.
if ((psNorm / sqrt(1 - std::pow(1 - cs, 2 * i))) < h)
if ((psNorm / std::sqrt(1 - std::pow(1.0 - cs, 2.0 * (double) i))) < h)
{
pc[idx1] = (1 - cc) * pc[idx0] + std::sqrt(cc * (2 - cc) *
muEffective) * step;
muEffective) * step;
if (iterate.n_rows > iterate.n_cols)
{
C[idx1] = (1 - c1 - cmu) * C[idx0] + c1 *
(pc[idx1] * pc[idx1].t());
(pc[idx1] * pc[idx1].t());
}
else
{
C[idx1] = (1 - c1 - cmu) * C[idx0] + c1 *
(pc[idx1].t() * pc[idx1]);
(pc[idx1].t() * pc[idx1]);
}
}
else
@@ -255,21 +295,19 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
{
for (size_t j = 0; j < mu; ++j)
{
C[idx1] = C[idx1] + cmu * w(j) *
pStep[idx(j)] * pStep[idx(j)].t();
C[idx1] = C[idx1] + cmu * w(j) * pStep[idx(j)] * pStep[idx(j)].t();
}
}
else
{
for (size_t j = 0; j < mu; ++j)
{
C[idx1] = C[idx1] + cmu * w(j) *
pStep[idx(j)].t() * pStep[idx(j)];
C[idx1] = C[idx1] + cmu * w(j) * pStep[idx(j)].t() * pStep[idx(j)];
}
}
arma::eig_sym(eigval, eigvec, C[idx1]);
const arma::uvec negativeEigval = arma::find(eigval < 0, 1);
eig_sym(eigval, eigvec, C[idx1]);
const UVecType negativeEigval = find(eigval < 0, 1);
if (!negativeEigval.is_empty())
{
if (negativeEigval(0) == 0)
@@ -279,7 +317,7 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
else
{
C[idx1] = eigvec.cols(0, negativeEigval(0) - 1) *
arma::diagmat(eigval.subvec(0, negativeEigval(0) - 1)) *
diagmat(eigval.subvec(0, negativeEigval(0) - 1)) *
eigvec.cols(0, negativeEigval(0) - 1).t();
}
}
@@ -293,22 +331,32 @@ typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
Warn << "CMA-ES: converged to " << overallObjective << "; "
<< "terminating with failure. Try a smaller step size?" << std::endl;
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
if (std::abs(lastObjective - overallObjective) < tolerance)
{
Info << "CMA-ES: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
if (steps > patience) {
Info << "CMA-ES: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
}
else {
steps = 0;
}
steps++;
lastObjective = overallObjective;
}
iterate = transformationPolicy.Transform(iterate);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
@@ -26,6 +26,7 @@ class FullSelection
* @tparam SeparableFunctionType Type of the function to be evaluated.
* @param function Function to optimize.
* @param batchSize Batch size to use for each step.
* @param terminate Whether optimization should be terminated after this call.
* @param iterate starting point.
*/
template<typename SeparableFunctionType,
@@ -34,6 +35,7 @@ class FullSelection
double Select(SeparableFunctionType& function,
const size_t batchSize,
const MatType& iterate,
bool& terminate,
CallbackTypes&... callbacks)
{
// Find the number of functions to use.
@@ -45,7 +47,8 @@ class FullSelection
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - f);
objective += function.Evaluate(iterate, f, effectiveBatchSize);
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
terminate |= Callback::Evaluate(*this, f, iterate, objective,
callbacks...);
}
return objective;
+181
View File
@@ -0,0 +1,181 @@
/**
* @file ipop_cmaes_impl.hpp
* @author Marcus Edel
* @author Benjami Parellada
*
* Definition of the IPOP Covariance Matrix Adaptation Evolution Strategy
* as proposed by A. Auger and N. Hansen in "A Restart CMA Evolution
* Strategy With Increasing Population Size" and BIPOP Covariance Matrix
* Adaptation Evolution Strategy as proposed by N. Hansen in "Benchmarking
* a BI-population CMA-ES on the BBOB-2009 function testbed".
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CMAES_POP_CMAES_HPP
#define ENSMALLEN_CMAES_POP_CMAES_HPP
#include "cmaes.hpp"
namespace ens {
/**
* Population-based CMA-ES (POP-CMA-ES) that can operate as either IPOP-CMA-ES
* or BIPOP-CMA-ES based on a flag.
*
* IPOP CMA-ES is a variant of the stochastic search algorithm
* CMA-ES - Covariance Matrix Adaptation Evolution Strategy.
* IPOP CMA-ES, also known as CMAES with increasing population size,
* incorporates a restart strategy that involves gradually increasing
* the population size. This approach is specifically designed to
* enhance the performance of CMA-ES on multi-modal functions.
*
* For more information, please refer to:
*
* @code
* @INPROCEEDINGS{1554902,
* author = {Auger, A. and Hansen, N.},
* booktitle = {2005 IEEE Congress on Evolutionary Computation},
* title = {A restart CMA evolution strategy with increasing population
* size},
* year = {2005},
* volume = {2},
* number = {},
* pages = {1769-1776 Vol. 2},
* doi = {10.1109/CEC.2005.1554902}}
* @endcode
*
* IPOP CMA-ES can optimize separable functions. For more details, see the
* documentation on function types included with this distribution or on the
* ensmallen website.
*
* BI-Population CMA-ES is a variant of the stochastic search algorithm
* CMA-ES - Covariance Matrix Adaptation Evolution Strategy.
* It implements a dual restart strategy with varying population sizes: one
* increasing and one with smaller, varied sizes. This BI-population approach
* is designed to optimize performance on multi-modal function testbeds by
* leveraging different exploration and exploitation dynamics.
*
* For more information, please refer to:
*
* @code
* @inproceedings{hansen2009benchmarking,
* title = {Benchmarking a BI-population CMA-ES on the BBOB-2009 function
* testbed},
* author = {Hansen, Nikolaus},
* booktitle = {Proceedings of the 11th annual conference companion on genetic
* and evolutionary computation conference: late breaking
* papers},
* pages = {2389--2396},
* year = {2009}}
* @endcode
*
* BI-Population CMA-ES can efficiently handle separable, multimodal, and weak
* structure functions across various dimensions, as demonstrated in the
* comprehensive results of the BBOB-2009 function testbed. The optimizer
* utilizes an interlaced multistart strategy to balance between broad
* exploration and intensive exploitation, adjusting population sizes and
* step-sizes dynamically.
*/
template<typename SelectionPolicyType = FullSelection,
typename TransformationPolicyType = EmptyTransformation<>,
bool UseBIPOPFlag = true>
class POP_CMAES : public CMAES<SelectionPolicyType, TransformationPolicyType>
{
public:
/**
* Construct the POP-CMA-ES optimizer with the given parameters.
* Other than the same CMA-ES parameters, it also adds the maximum number of
* restarts, the increase in population factor, the maximum number of
* evaluations, as well as a flag indicating to use BIPOP or not.
* The suggested values are not necessarily good for the given problem, so it
* is suggested that the values used be tailored to the task at hand. The
* maximum number of iterations refers to the maximum number of points that
* are processed (i.e., one iteration equals one point; one iteration does not
* equal one pass over the dataset).
*
* @param lambda The initial population size (0 use the default size).
* @param transformationPolicy Instantiated transformation policy used to
* map the coordinates to the desired domain.
* @param batchSize Batch size to use for the objective calculation.
* @param maxIterations Maximum number of iterations allowed.
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param selectionPolicy Instantiated selection policy used to calculate the
* objective.
* @param stepSize Starting sigma/step size (will be modified).
* @param populationFactor The factor by which population increases
* after each restart.
* @param maxRestarts Maximum number of restarts.
* @param maxFunctionEvaluations Maximum number of function evaluations.
*/
POP_CMAES(const size_t lambda = 0,
const TransformationPolicyType& transformationPolicy =
TransformationPolicyType(),
const size_t batchSize = 32,
const size_t maxIterations = 1000,
const double tolerance = 1e-5,
const SelectionPolicyType& selectionPolicy = SelectionPolicyType(),
double stepSize = 0,
const size_t maxRestarts = 9,
const double populationFactor = 2,
const size_t maxFunctionEvaluations = 1e9);
/**
* Set POP-CMA-ES specific parameters.
*/
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Get the population factor.
double PopulationFactor() const { return populationFactor; }
//! Modify the population factor.
double& PopulationFactor() { return populationFactor; }
//! Get the maximum number of restarts.
size_t MaxRestarts() const { return maxRestarts; }
//! Modify the maximum number of restarts.
size_t& MaxRestarts() { return maxRestarts; }
//! Get the maximum number of function evaluations.
size_t MaxFunctionEvaluations() const { return maxFunctionEvaluations; }
//! Modify the maximum number of function evaluations.
size_t& MaxFunctionEvaluations() { return maxFunctionEvaluations; }
//! Get the BIPOP mode flag.
static constexpr bool UseBIPOP() { return UseBIPOPFlag; }
private:
//! Population factor
double populationFactor;
//! Maximum number of restarts.
size_t maxRestarts;
//! Maximum number of function evaluations.
size_t maxFunctionEvaluations;
};
// Define IPOP_CMAES and BIPOP_CMAES using the POP_CMAES template
template<typename SelectionPolicyType = FullSelection,
typename TransformationPolicyType = EmptyTransformation<>>
using IPOP_CMAES = POP_CMAES<
SelectionPolicyType, TransformationPolicyType, false>;
template<typename SelectionPolicyType = FullSelection,
typename TransformationPolicyType = EmptyTransformation<>>
using BIPOP_CMAES = POP_CMAES<
SelectionPolicyType, TransformationPolicyType, true>;
} // namespace ens
// Include implementation.
#include "pop_cmaes_impl.hpp"
#endif
@@ -0,0 +1,163 @@
/**
* @file ipop_cmaes_impl.hpp
* @author Marcus Edel
* @author Benjami Parellada
*
* Implementation of the IPOP Covariance Matrix Adaptation Evolution Strategy
* as proposed by A. Auger and N. Hansen in "A Restart CMA Evolution
* Strategy With Increasing Population Size" and BIPOP Covariance Matrix
* Adaptation Evolution Strategy as proposed by N. Hansen in "Benchmarking
* a BI-population CMA-ES on the BBOB-2009 function testbed".
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CMAES_POP_CMAES_IMPL_HPP
#define ENSMALLEN_CMAES_POP_CMAES_IMPL_HPP
#include "pop_cmaes.hpp"
#include <ensmallen_bits/function.hpp>
namespace ens {
template<typename SelectionPolicyType, typename TransformationPolicyType, bool UseBIPOPFlag>
POP_CMAES<SelectionPolicyType,
TransformationPolicyType,
UseBIPOPFlag>::POP_CMAES(
const size_t lambda,
const TransformationPolicyType& transformationPolicy,
const size_t batchSize,
const size_t maxIterations,
const double tolerance,
const SelectionPolicyType& selectionPolicy,
double stepSize,
const size_t maxRestarts,
const double populationFactor,
const size_t maxFunctionEvaluations) :
CMAES<SelectionPolicyType, TransformationPolicyType>(
lambda, transformationPolicy, batchSize, maxIterations,
tolerance, selectionPolicy, stepSize),
populationFactor(populationFactor),
maxRestarts(maxRestarts),
maxFunctionEvaluations(maxFunctionEvaluations)
{ /* Nothing to do. */ }
template<typename SelectionPolicyType,
typename TransformationPolicyType,
bool UseBIPOPFlag>
template<typename SeparableFunctionType, typename MatType, typename... CallbackTypes>
typename MatType::elem_type POP_CMAES<SelectionPolicyType,
TransformationPolicyType, UseBIPOPFlag>::Optimize(
SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
StoreBestCoordinates<MatType> sbc;
StoreBestCoordinates<MatType> overallSBC;
size_t totalFunctionEvaluations = 0;
size_t largePopulationBudget = 0;
size_t smallPopulationBudget = 0;
// First single run with default population size
MatType iterate = iterateIn;
ElemType overallObjective = CMAES<SelectionPolicyType,
TransformationPolicyType>::Optimize(function, iterate, sbc,
callbacks...);
overallSBC = sbc;
ElemType objective;
size_t evaluations;
size_t defaultLambda = this->PopulationSize();
size_t currentLargeLambda = defaultLambda;
double stepSizeDefault = this->StepSize();
// Print out the default population size
Info << "Default population size: " << defaultLambda << "." << std::endl;
size_t restart = 0;
while (restart < maxRestarts)
{
if (!UseBIPOPFlag || largePopulationBudget <= smallPopulationBudget ||
restart == 0 || restart == maxRestarts - 1)
{
// Large population regime (IPOP or BIPOP)
currentLargeLambda *= populationFactor;
this->PopulationSize() = currentLargeLambda;
this->StepSize() = stepSizeDefault;
Info << "POP-CMA-ES: restart " << restart << ", large population size" <<
" (lambda): " << this->PopulationSize() << "." << std::endl;
iterate = iterateIn;
// Optimize using the CMAES object.
objective = CMAES<SelectionPolicyType,
TransformationPolicyType>::Optimize(function, iterate, sbc,
callbacks...);
evaluations = this->FunctionEvaluations();
largePopulationBudget += evaluations;
}
else if (UseBIPOPFlag)
{
// Small population regime (BIPOP only)
double u = arma::randu<double>();
size_t smallLambda = static_cast<size_t>(defaultLambda * std::pow(0.5 *
currentLargeLambda / defaultLambda, u * u));
double stepSizeSmall = 2 * std::pow(10, -2 * arma::randu<double>());
this->PopulationSize() = smallLambda;
this->StepSize() = stepSizeSmall;
Info << "BIPOP-CMA-ES: restart " << restart << ", small population" <<
" size (lambda): " << this->PopulationSize() << "." << std::endl;
iterate = iterateIn;
// Optimize using the CMAES object.
objective = CMAES<SelectionPolicyType,
TransformationPolicyType>::Optimize(function, iterate, sbc,
callbacks...);
evaluations = this->FunctionEvaluations();
smallPopulationBudget += evaluations;
}
if (objective < overallObjective)
{
overallObjective = objective;
overallSBC = sbc;
Info << "POP-CMA-ES: New best objective: " << overallObjective
<< "." << std::endl;
}
totalFunctionEvaluations += evaluations;
// Check if the total number of evaluations has exceeded the limit
if (totalFunctionEvaluations >= maxFunctionEvaluations) {
Warn << "POP-CMA-ES: Maximum function overall evaluations reached. "
<< "terminating optimization." << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
iterateIn = std::move(overallSBC.BestCoordinates());
return overallSBC.BestObjective();
}
++restart;
}
Callback::EndOptimization(*this, function, iterate, callbacks...);
iterateIn = std::move(overallSBC.BestCoordinates());
return overallSBC.BestObjective();
}
} // namespace ens
#endif
@@ -41,6 +41,7 @@ class RandomSelection
* @tparam SeparableFunctionType Type of the function to be evaluated.
* @param function Function to optimize.
* @param batchSize Batch size to use for each step.
* @param terminate Whether optimization should be terminated after this call.
* @param iterate starting point.
*/
template<typename SeparableFunctionType,
@@ -49,6 +50,7 @@ class RandomSelection
double Select(SeparableFunctionType& function,
const size_t batchSize,
const MatType& iterate,
bool& terminate,
CallbackTypes&... callbacks)
{
// Find the number of functions to use.
@@ -64,7 +66,8 @@ class RandomSelection
objective += function.Evaluate(iterate, selection, effectiveBatchSize);
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
terminate |= Callback::Evaluate(*this, f, iterate, objective,
callbacks...);
}
return objective;
@@ -0,0 +1,162 @@
/**
* @file boundary_box_constraint.hpp
* @author Suvarsha Chennareddy
*
* Boundary Box Transformation.
*
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CMAES_BOUNDARY_BOX_TRANSFORMATION_HPP
#define ENSMALLEN_CMAES_BOUNDARY_BOX_TRANSFORMATION_HPP
namespace ens {
/**
* More often than not, coordinates must be bounded by some constraints.
* In a particular case, the domain of a specific function is restricted
* by boundaries.
* The implemented transformation transforms given coordinates into a region
* bounded by the given lower and upper bounds (a box). First, the
* coordinates are shifted into a feasible preimage bounded by lowerBound - al
* and upperBound + au where al and au and calculated internally.
* These shifted coordinates are then transformed into coordinates bounded by
* lower_bound and upper_bound. It is an identity transformation in between
* the lower and upper bounds.
*
* For more information, check the original implementation in C by N. Hansen:
* https://github.com/CMA-ES/c-cmaes/blob/master/src/boundary_transformation.c
*
* @tparam MatType The matrix type of the coordinates and bounds.
*/
template<typename MatType = arma::mat>
class BoundaryBoxConstraint
{
public:
/**
* Construct the boundary box constraint policy.
*/
BoundaryBoxConstraint()
{ /* Nothing to do. */ }
/**
* Construct the boundary box constraint policy.
*
* @param lowerBound The lower bound of the coordinates.
* @param upperBound The upper bound of the coordinates.
*/
BoundaryBoxConstraint(const MatType& lowerBound,
const MatType& upperBound) :
lowerBound(lowerBound),
upperBound(upperBound)
{}
/**
* Construct the boundary box constraint policy.
*
* @param lowerBound The lower bound (for every dimension) of the coordinates.
* @param upperBound The upper bound (for every dimension) of the coordinates.
*/
BoundaryBoxConstraint(const typename MatType::elem_type lowerBound,
const typename MatType::elem_type upperBound) :
lowerBound({ (typename MatType::elem_type) lowerBound }),
upperBound({ (typename MatType::elem_type) upperBound })
{}
/**
* Map the given coordinates to the range
* [lowerBound, upperBound]
*
* @param x Given coordinates.
* @return Transformed coordinates.
*/
MatType Transform(const MatType& x)
{
typedef typename MatType::elem_type ElemType;
double diff, al, au, xlow, xup, r;
size_t Bi, Bj;
MatType y = x;
for (size_t i = 0; i < x.n_rows; i++)
{
Bi = (i < lowerBound.n_rows) ? i : (lowerBound.n_rows - 1);
for (size_t j = 0; j < x.n_cols; j++)
{
Bj = (j < lowerBound.n_cols) ? j : (lowerBound.n_cols - 1);
diff = (upperBound(Bi, Bj) - lowerBound(Bi, Bj)) / 2.0;
al = std::min(diff, (1 + std::abs(lowerBound(Bi, Bj))) / 20.0);
au = std::min(diff, (1 + std::abs(upperBound(Bi, Bj))) / 20.0);
xlow = lowerBound(Bi, Bj) - 2 * al - diff;
xup = upperBound(Bi, Bj) + 2 * au + diff;
r = 2 * (2 * diff + al + au);
// Shift y into feasible pre-image.
if (y(i, j) < xlow)
{
y(i,j) += (ElemType)(r * (1 + (int)((xlow - y(i, j)) / r)));
}
if (y(i, j) > xup)
{
y(i, j) -= (ElemType)(r * (1 + (int)((y(i, j) - xup) / r)));
}
if (y(i, j) < lowerBound(Bi, Bj) - al)
{
y(i, j) += (ElemType)(2 * (lowerBound(Bi, Bj) - al - y(i, j)));
}
if (y(i, j) > upperBound(Bi, Bj) + au)
{
y(i, j) -= (ElemType)(2 * (y(i, j) - upperBound(Bi, Bj) - au));
}
// Boundary transformation.
if (y(i, j) < lowerBound(Bi, Bj) + al)
{
y(i, j) = (ElemType)(lowerBound(Bi, Bj) +
(y(i, j) - (lowerBound(Bi, Bj) - al)) *
(y(i, j) - (lowerBound(Bi, Bj) - al)) / 4.0 / al);
}
else if (y(i,j) > upperBound(Bi,Bj) - au)
{
y(i, j) = (ElemType)(upperBound(Bi, Bj) -
(y(i, j) - (upperBound(Bi, Bj) + au)) *
(y(i, j) - (upperBound(Bi, Bj) + au)) / 4.0 / au);
}
}
}
return y;
}
/**
* Return a suitable initial step size.
*
* @return initial step size.
*/
typename MatType::elem_type InitialStepSize()
{ return 0.3 * (upperBound - lowerBound).min(); }
//! Get the lower bound of decision variables.
MatType LowerBound() const { return lowerBound; }
//! Modify the lower bound of decision variables.
MatType& LowerBound() { return lowerBound; }
//! Get the upper bound of decision variables.
MatType UpperBound() const { return upperBound; }
//! Modify the upper bound of decision variables.
MatType& UpperBound() { return upperBound; }
private:
//! Lower bound of decision variables.
MatType lowerBound;
//! Upper bound of decision variables.
MatType upperBound;
};
} // namespace ens
#endif
@@ -0,0 +1,47 @@
/**
* @file empty_transformation.hpp
* @author Suvarsha Chennareddy
*
* Empty Transformation, can also be called an Indentity Transformation.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_CMAES_EMPTY_TRANSFORMATION_HPP
#define ENSMALLEN_CMAES_EMPTY_TRANSFORMATION_HPP
namespace ens {
/**
* This is an empty transformation. As the name indicates, it does
* not do anything. It is essentially an identity
* transformation and is meant to be used when there are no
* sorts of constraints on the coordinates.
*
* @tparam MatType The matrix type of the coordinates.
*/
template<typename MatType = arma::mat>
class EmptyTransformation
{
public:
/**
* Transforms coordinates to themselves (effectively no transformation).
*
* @param x Input coordinates.
* @return Transformed coordinates (the coordinates themselves).
*/
MatType Transform(const MatType& x) { return x; }
/**
* Return a suitable initial step size.
*
* @return initial step size.
*/
typename MatType::elem_type InitialStepSize() { return 1; }
};
} // namespace ens
#endif
+4 -4
View File
@@ -149,14 +149,14 @@ class CNE
private:
//! Reproduce candidates to create the next generation.
template<typename MatType>
template<typename MatType, typename IndexType>
void Reproduce(std::vector<MatType>& population,
const MatType& fitnessValues,
arma::uvec& index);
IndexType& index);
//! Modify weights with some noise for the evolution of next generation.
template<typename MatType>
void Mutate(std::vector<MatType>& population, arma::uvec& index);
template<typename MatType, typename IndexType>
void Mutate(std::vector<MatType>& population, IndexType& index);
/**
* Crossover parents and create new childs. Two parents create two new childs.
+30 -24
View File
@@ -47,6 +47,7 @@ typename MatType::elem_type CNE::Optimize(ArbitraryFunctionType& function,
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename ForwardType<MatType>::uvec UVecType;
// Make sure that we have the methods that we need. Long name...
traits::CheckArbitraryFunctionTypeAPI<ArbitraryFunctionType,
@@ -56,7 +57,7 @@ typename MatType::elem_type CNE::Optimize(ArbitraryFunctionType& function,
// Vector of fitness values corresponding to each candidate.
BaseMatType fitnessValues;
//! Index of sorted fitness values.
arma::uvec index;
UVecType index;
// Make sure for evolution to work at least four candidates are present.
if (populationSize < 4)
@@ -93,8 +94,8 @@ typename MatType::elem_type CNE::Optimize(ArbitraryFunctionType& function,
std::vector<BaseMatType> population;
for (size_t i = 0 ; i < populationSize; ++i)
{
population.push_back(arma::randn<BaseMatType>(iterate.n_rows,
iterate.n_cols) + iterate);
population.push_back(BaseMatType(iterate.n_rows, iterate.n_cols,
GetFillType<MatType>::randn) + iterate);
}
// Store the number of elements in the objective matrix.
@@ -111,26 +112,26 @@ typename MatType::elem_type CNE::Optimize(ArbitraryFunctionType& function,
// Find the fitness before optimization using given iterate parameters.
ElemType lastBestFitness = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, lastBestFitness, callbacks...);
terminate |= Callback::Evaluate(*this, function, iterate, lastBestFitness,
callbacks...);
// Iterate until maximum number of generations is obtained.
terminate |= Callback::BeginOptimization(*this, function, iterate,
callbacks...);
Callback::BeginOptimization(*this, function, iterate, callbacks...);
for (size_t gen = 1; gen <= maxGenerations && !terminate; gen++)
{
// Calculating fitness values of all candidates.
for (size_t i = 0; i < populationSize; i++)
{
// Select a candidate and insert the parameters in the function.
iterate = population[i];
terminate |= Callback::StepTaken(*this, function, iterate,
callbacks...);
// Select a candidate and insert the parameters in the function.
iterate = population[i];
terminate |= Callback::StepTaken(*this, function, iterate,
callbacks...);
// Find fitness of candidate.
fitnessValues[i] = function.Evaluate(iterate);
// Find fitness of candidate.
fitnessValues[i] = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, fitnessValues[i],
callbacks...);
terminate |= Callback::Evaluate(*this, function, iterate,
fitnessValues[i], callbacks...);
}
Info << "Generation number: " << gen << " best fitness = "
@@ -154,21 +155,23 @@ typename MatType::elem_type CNE::Optimize(ArbitraryFunctionType& function,
// Set the best candidate into the network parameters.
iterateIn = population[index(0)];
// The output of the callback doesn't matter because the optimization is
// finished.
const ElemType objective = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, objective, callbacks...);
(void) Callback::Evaluate(*this, function, iterate, objective, callbacks...);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return objective;
}
//! Reproduce candidates to create the next generation.
template<typename MatType>
template<typename MatType, typename IndexType>
inline void CNE::Reproduce(std::vector<MatType>& population,
const MatType& fitnessValues,
arma::uvec& index)
IndexType& index)
{
// Sort fitness values. Smaller fitness value means better performance.
index = arma::sort_index(fitnessValues);
index = sort_index(fitnessValues);
// First parent.
size_t mom;
@@ -239,17 +242,20 @@ inline void CNE::Crossover(std::vector<MatType>& population,
}
//! Modify weights with some noise for the evolution of next generation.
template<typename MatType>
inline void CNE::Mutate(std::vector<MatType>& population, arma::uvec& index)
template<typename MatType, typename IndexType>
inline void CNE::Mutate(std::vector<MatType>& population, IndexType& index)
{
typedef typename MatType::elem_type ElemType;
// Mutate the whole matrix with the given rate and probability.
// The best candidate is not altered.
for (size_t i = 1; i < populationSize; i++)
{
population[index(i)] += (arma::randu<MatType>(population[index(i)].n_rows,
population[index(i)].n_cols) < mutationProb) %
(mutationSize * arma::randn<MatType>(population[index(i)].n_rows,
population[index(i)].n_cols));
population[index(i)] += conv_to<MatType>::from(
randu<MatType>(population[index(i)].n_rows,
population[index(i)].n_cols) < ElemType(mutationProb)) %
(ElemType(mutationSize) * MatType(population[index(i)].n_rows,
population[index(i)].n_cols, GetFillType<MatType>::randn));
}
}
+4 -14
View File
@@ -56,18 +56,8 @@
#endif
// Define ens_deprecated for deprecated functionality.
// This is adapted from Armadillo's implementation.
#if defined(_MSC_VER)
#define ens_deprecated __declspec(deprecated)
#elif defined(__GNUG__) && (!defined(__clang__))
#define ens_deprecated __attribute__((__deprecated__))
#elif defined(__clang__)
#if __has_attribute(__deprecated__)
#define ens_deprecated __attribute__((__deprecated__))
#else
#define ens_deprecated
#endif
#else
#define ens_deprecated
// undefine conflicting macros
#if defined(As)
#pragma message ("WARNING: undefined conflicting 'As' macro")
#undef As
#endif
+4 -4
View File
@@ -45,10 +45,10 @@ namespace ens {
*
* @code
* @techreport{storn1995,
* title = {Differential Evolutiona simple and efficient adaptive scheme
* for global optimization over continuous spaces},
* author = {Storn, Rainer and Price, Kenneth},
* year = 1995
* title = {Differential Evolutiona simple and efficient adaptive scheme
* for global optimization over continuous spaces},
* author = {Storn, Rainer and Price, Kenneth},
* year = 1995
* }
* @endcode
*
+22 -15
View File
@@ -40,14 +40,16 @@ typename MatType::elem_type DE::Optimize(FunctionType& function,
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename ForwardType<MatType>::vec ColType;
BaseMatType& iterate = (BaseMatType&) iterateIn;
// Population matrix. Each column is a candidate.
std::vector<BaseMatType> population;
population.resize(populationSize);
// Vector of fitness values corresponding to each candidate.
arma::Col<ElemType> fitnessValues;
ColType fitnessValues;
// Make sure that we have the methods that we need. Long name...
traits::CheckArbitraryFunctionTypeAPI<
@@ -57,13 +59,13 @@ typename MatType::elem_type DE::Optimize(FunctionType& function,
// Population Size must be at least 3 for DE to work.
if (populationSize < 3)
{
throw std::logic_error("CNE::Optimize(): population size should be at least"
throw std::logic_error("DE::Optimize(): population size should be at least"
" 3!");
}
// Initialize helper variables.
fitnessValues.set_size(populationSize);
ElemType lastBestFitness = DBL_MAX;
ElemType lastBestFitness = std::numeric_limits<ElemType>::max();
BaseMatType bestElement;
// Controls early termination of the optimization process.
@@ -77,19 +79,18 @@ typename MatType::elem_type DE::Optimize(FunctionType& function,
population[i] += iterate;
fitnessValues[i] = function.Evaluate(population[i]);
Callback::Evaluate(*this, function, population[i], fitnessValues[i],
callbacks...);
terminate |= Callback::Evaluate(*this, function, population[i],
fitnessValues[i], callbacks...);
if (fitnessValues[i] < lastBestFitness)
{
lastBestFitness = fitnessValues[i];
lastBestFitness = ElemType(fitnessValues[i]);
bestElement = population[i];
}
}
// Iterate until maximum number of generations are completed.
terminate |= Callback::BeginOptimization(*this, function, iterate,
callbacks...);
Callback::BeginOptimization(*this, function, iterate, callbacks...);
for (size_t gen = 0; gen < maxGenerations && !terminate; gen++)
{
// Generate new population based on /best/1/bin strategy.
@@ -112,24 +113,30 @@ typename MatType::elem_type DE::Optimize(FunctionType& function,
while (m == member && m == l);
// Generate new "mutant" from two randomly chosen members.
BaseMatType mutant = bestElement + differentialWeight *
BaseMatType mutant = bestElement + ElemType(differentialWeight) *
(population[l] - population[m]);
// Perform crossover.
const BaseMatType cr = arma::randu<BaseMatType>(iterate.n_rows);
BaseMatType cr;
cr.randu(iterate.n_rows, 1);
for (size_t it = 0; it < iterate.n_rows; it++)
{
if (cr[it] >= crossoverRate)
if (cr[it] >= ElemType(crossoverRate))
{
mutant[it] = iterate[it];
mutant(it) = ElemType(iterate(it));
}
}
ElemType iterateValue = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, iterateValue, callbacks...);
terminate |= Callback::Evaluate(*this, function, iterate, iterateValue,
callbacks...);
const ElemType mutantValue = function.Evaluate(mutant);
Callback::Evaluate(*this, function, mutant, mutantValue, callbacks...);
terminate |= Callback::Evaluate(*this, function, mutant, mutantValue,
callbacks...);
if (terminate)
break;
// Replace the current member if mutant is better.
if (mutantValue < iterateValue)
@@ -154,7 +161,7 @@ typename MatType::elem_type DE::Optimize(FunctionType& function,
}
// Update helper variables.
lastBestFitness = fitnessValues.min();
lastBestFitness = ElemType(fitnessValues.min());
for (size_t it = 0; it < populationSize; it++)
{
if (fitnessValues[it] == lastBestFitness)
@@ -0,0 +1,179 @@
/**
* @file delta_bar_delta.hpp
* @author Ranjodh Singh
*
* Class wrapper for the DeltaBarDelta update policy.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_DELTA_BAR_DELTA_HPP
#define ENSMALLEN_DELTA_BAR_DELTA_HPP
#include <ensmallen_bits/gradient_descent/gradient_descent.hpp>
#include "update_policies/delta_bar_delta_update.hpp"
namespace ens {
/**
* DeltaBarDelta optimizer.
*
* A heuristic designed to accelerate convergence by
* adapting the learning rate of each parameter individually.
*
* According to the Delta-Bar-Delta update:
*
* - If the current gradient and the exponential average of
* past gradients corresponding to a parameter have the same
* sign, then the step size for that parameter is incremented by
* \f$\kappa\f$. Otherwise, it is decreased by a proportion \f$\phi\f$
* of its current value (additive increase, multiplicative decrease).
*
* @note This implementation uses a minStepSize parameter to set a lower
* bound for the learning rate. This prevents the learning rate from
* dropping to zero, which can occur due to floating-point underflow.
* For tasks which require extreme fine-tuning, you may need to lower
* this parameter below its default value (1e-8) in order to allow for
* smaller learning rates.
*
* @code
* @article{jacobs1988increased,
* title = {Increased Rates of Convergence Through Learning Rate
* Adaptation},
* author = {Jacobs, Robert A.},
* journal = {Neural Networks},
* volume = {1},
* number = {4},
* pages = {295--307},
* year = {1988},
* publisher = {Pergamon}
* }
* @endcode
*/
class DeltaBarDelta
{
public:
/**
* Construct the DeltaBarDelta optimizer with the given function and
* parameters. DeltaBarDelta is very sensitive to its parameters (kappa
* and phi) hence a good hyperparameter selection is necessary as its
* default may not fit every case.
*
* @param stepSize Step size (initial).
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param kappa Additive increase constant for step size.
* @param phi Multiplicative decrease factor for step size.
* @param theta Decay rate for the exponential moving average.
* @param minStepSize Minimum allowed step size for any parameter
* (default: 1e-8).
* @param resetPolicy If true, parameters are reset before every Optimize
* call; otherwise, their values are retained.
*/
DeltaBarDelta(const double stepSize = 1.0,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const double kappa = 0.2,
const double phi = 0.2,
const double theta = 0.5,
const double minStepSize = 1e-8,
const bool resetPolicy = true);
/**
* Optimize the given function using DeltaBarDelta.
* The given starting point will be modified to store the finishing
* point of the algorithm, and the final objective value is returned.
*
* @tparam SeparableFunctionType Type of the function to optimize.
* @tparam MatType Type of matrix to optimize with.
* @tparam GradType Type of matrix to use to represent function gradients.
* @tparam CallbackTypes Types of callback functions.
* @param function Function to optimize.
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize<SeparableFunctionType, MatType, GradType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Forward the MatType as GradType.
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<SeparableFunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the initial step size.
double StepSize() const { return optimizer.StepSize(); }
//! Modify the initial step size.
double& StepSize() { return optimizer.StepSize(); }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return optimizer.MaxIterations(); }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return optimizer.MaxIterations(); }
//! Get the additive increase constant for step size.
double Kappa() const { return optimizer.UpdatePolicy().Kappa(); }
//! Modify the additive increase constant for step size.
double& Kappa() { return optimizer.UpdatePolicy().Kappa(); }
//! Get the multiplicative decrease factor for step size.
double Phi() const { return optimizer.UpdatePolicy().Phi(); }
//! Modify the multiplicative decrease factor for step size.
double& Phi() { return optimizer.UpdatePolicy().Phi(); }
//! Get the decay rate for the exponential moving average.
double Theta() const { return optimizer.UpdatePolicy().Theta(); }
//! Modify the decay rate for the exponential moving average.
double& Theta() { return optimizer.UpdatePolicy().Theta(); }
//! Get the minimum allowed step size for any parameter.
double MinStepSize() const { return optimizer.UpdatePolicy().MinStepSize(); }
//! Modify the minimum allowed step size for any parameter.
double& MinStepSize() { return optimizer.UpdatePolicy().MinStepSize(); }
//! Get the tolerance for termination.
double Tolerance() const { return optimizer.Tolerance(); }
//! Modify the tolerance for termination.
double& Tolerance() { return optimizer.Tolerance(); }
//! Get whether or not the update policy parameters are reset before
//! Optimize call.
bool ResetPolicy() const { return optimizer.ResetPolicy(); }
//! Modify whether or not the update policy parameters are reset before
//! Optimize call.
bool& ResetPolicy() { return optimizer.ResetPolicy(); }
private:
//! The GradientDescentType object with DeltaBarDelta policy.
GradientDescentType<DeltaBarDeltaUpdate, NoDecay> optimizer;
};
} // namespace ens
// Include implementation.
#include "delta_bar_delta_impl.hpp"
#endif // ENSMALLEN_DELTA_BAR_DELTA_HPP
@@ -0,0 +1,41 @@
/**
* @file delta_bar_delta_impl.hpp
* @author Ranjodh Singh
*
* Implementation of DeltaBarDelta class wrapper.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_DELTA_BAR_DELTA_IMPL_HPP
#define ENSMALLEN_DELTA_BAR_DELTA_IMPL_HPP
// In case it hasn't been included yet.
#include "./delta_bar_delta.hpp"
namespace ens {
inline DeltaBarDelta::DeltaBarDelta(
const double stepSize,
const size_t maxIterations,
const double tolerance,
const double kappa,
const double phi,
const double theta,
const double minStepSize,
const bool resetPolicy) :
optimizer(stepSize,
maxIterations,
tolerance,
DeltaBarDeltaUpdate(stepSize, kappa, phi, theta, minStepSize),
NoDecay(),
resetPolicy)
{
/* Nothing to do. */
}
} // namespace ens
#endif // ENSMALLEN_DELTA_BAR_DELTA_IMPL_HPP
@@ -0,0 +1,183 @@
/**
* @file momentum_delta_bar_delta.hpp
* @author Ranjodh Singh
*
* Class wrapper for the MomentumDeltaBarDelta update policy.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_HPP
#define ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_HPP
#include <ensmallen_bits/gradient_descent/gradient_descent.hpp>
#include "update_policies/momentum_delta_bar_delta_update.hpp"
namespace ens {
/**
* MomentumDeltaBarDelta Optimizer.
*
* A DeltaBarDelta variant that incorporates the following modifications:
* - In the original DeltaBarDelta, the momentum term (delta_bar) is used
* solely for sign comparison with the current gradient and does not
* participate in the parameter update. In this modified variant, the
* momentum term (velocity) is directly used to update the parameters.
* - Instead of adjusting the step size directly, each parameter maintains
* a gain value initialized to 1.0. Updates apply additive increases or
* multiplicative decreases to this gain. The effective step size for a
* parameter is the product of its initial step size and its current gain.
*
* Note: This variant originates from optimization of the t-SNE cost function.
*
* @code
* @article{maaten2008visualizing,
* title={Visualizing data using t-SNE},
* author={van der Maaten, Laurens and Hinton, Geoffrey},
* journal={Journal of machine learning research},
* volume={9},
* pages={2579--2605},
* month={11},
* year={2008}
* }
* @endcode
*
* @code
* @article{jacobs1988increased,
* title = {Increased Rates of Convergence Through Learning Rate
* Adaptation},
* author = {Jacobs, Robert A.},
* journal = {Neural Networks},
* volume = {1},
* number = {4},
* pages = {295--307},
* year = {1988},
* publisher = {Pergamon}
* }
* @endcode
*/
class MomentumDeltaBarDelta
{
public:
/**
* Construct the MomentumDeltaBarDelta optimizer with the given function and
* parameters.
*
* @param stepSize Step size (initial).
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param kappa Additive increase constant for step size.
* @param phi Multiplicative decrease factor for step size.
* @param momentum The momentum decay hyperparameter.
* @param minGain Minimum allowed gain (scaling factor) for any parameter
* (default: 1e-8).
* @param resetPolicy If true, parameters are reset before every Optimize
* call; otherwise, their values are retained.
*/
MomentumDeltaBarDelta(const double stepSize = 1.0,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const double kappa = 0.2,
const double phi = 0.8,
const double momentum = 0.5,
const double minGain = 1e-8,
const bool resetPolicy = true);
/**
* Optimize the given function using MomentumDeltaBarDelta.
* The given starting point will be modified to store the finishing
* point of the algorithm, and the final objective value is returned.
*
* @tparam SeparableFunctionType Type of the function to optimize.
* @tparam MatType Type of matrix to optimize with.
* @tparam GradType Type of matrix to use to represent function gradients.
* @tparam CallbackTypes Types of callback functions.
* @param function Function to optimize.
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize<SeparableFunctionType, MatType, GradType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Forward the MatType as GradType.
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<SeparableFunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the initial step size.
double StepSize() const { return optimizer.StepSize(); }
//! Modify the initial step size.
double& StepSize() { return optimizer.StepSize(); }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return optimizer.MaxIterations(); }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return optimizer.MaxIterations(); }
//! Get the additive increase constant for step size.
double Kappa() const { return optimizer.UpdatePolicy().Kappa(); }
//! Modify the additive increase constant for step size.
double& Kappa() { return optimizer.UpdatePolicy().Kappa(); }
//! Get the multiplicative decrease factor for step size.
double Phi() const { return optimizer.UpdatePolicy().Phi(); }
//! Modify the multiplicative decrease factor for step size.
double& Phi() { return optimizer.UpdatePolicy().Phi(); }
//! Get the momentum decay hyperparameter.
double Momentum() const { return optimizer.UpdatePolicy().Momentum(); }
//! Modify the momentum decay hyperparameter.
double& Momentum() { return optimizer.UpdatePolicy().Momentum(); }
//! Get the minimum allowed gain (scaling factor) for any parameter.
double MinGain() const { return optimizer.UpdatePolicy().MinGain(); }
//! Modify the minimum allowed gain (scaling factor) for any parameter.
double& MinGain() { return optimizer.UpdatePolicy().MinGain(); }
//! Get the tolerance for termination.
double Tolerance() const { return optimizer.Tolerance(); }
//! Modify the tolerance for termination.
double& Tolerance() { return optimizer.Tolerance(); }
//! Get whether or not the update policy parameters are reset before
//! Optimize call.
bool ResetPolicy() const { return optimizer.ResetPolicy(); }
//! Modify whether or not the update policy parameters are reset before
//! Optimize call.
bool& ResetPolicy() { return optimizer.ResetPolicy(); }
private:
//! The GradientDescentType object with MomentumDeltaBarDelta policy.
GradientDescentType<MomentumDeltaBarDeltaUpdate, NoDecay> optimizer;
};
} // namespace ens
// Include implementation.
#include "momentum_delta_bar_delta_impl.hpp"
#endif // ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_HPP
@@ -0,0 +1,41 @@
/**
* @file momentum_delta_bar_delta_impl.hpp
* @author Ranjodh Singh
*
* Implementation of MomentumDeltaBarDelta class wrapper.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_IMPL_HPP
#define ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_IMPL_HPP
// In case it hasn't been included yet.
#include "./momentum_delta_bar_delta.hpp"
namespace ens {
inline MomentumDeltaBarDelta::MomentumDeltaBarDelta(
const double stepSize,
const size_t maxIterations,
const double tolerance,
const double kappa,
const double phi,
const double momentum,
const double minGain,
const bool resetPolicy) :
optimizer(stepSize,
maxIterations,
tolerance,
MomentumDeltaBarDeltaUpdate(kappa, phi, momentum, minGain),
NoDecay(),
resetPolicy)
{
/* Nothing to do. */
}
} // namespace ens
#endif // ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_IMPL_HPP
@@ -0,0 +1,197 @@
/**
* @file delta_bar_delta_update.hpp
* @author Ranjodh Singh
*
* DeltaBarDelta update policy for Gradient Descent.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_DELTA_BAR_DELTA_UPDATE_HPP
#define ENSMALLEN_DELTA_BAR_DELTA_UPDATE_HPP
namespace ens {
/**
* DeltaBarDelta update policy for Gradient Descent.
*
* A heuristic designed to accelerate convergence by
* adapting the learning rate of each parameter individually.
*
* According to the Delta-Bar-Delta update:
*
* - If the current gradient and the exponential average of
* past gradients corresponding to a parameter have the same
* sign, then the step size for that parameter is incremented by
* \f$\kappa\f$. Otherwise, it is decreased by a proportion \f$\phi\f$
* of its current value (additive increase, multiplicative decrease).
*
* @note This implementation uses a minStepSize parameter to set a lower
* bound for the learning rate. This prevents the learning rate from
* dropping to zero, which can occur due to floating-point underflow.
* For tasks which require extreme fine-tuning, you may need to lower
* this parameter below its default value (1e-8) in order to allow for
* smaller learning rates.
*
* @code
* @article{jacobs1988increased,
* title = {Increased Rates of Convergence Through Learning Rate
* Adaptation},
* author = {Jacobs, Robert A.},
* journal = {Neural Networks},
* volume = {1},
* number = {4},
* pages = {295--307},
* year = {1988},
* publisher = {Pergamon}
* }
* @endcode
*/
class DeltaBarDeltaUpdate
{
public:
/**
* Construct the DeltaBarDelta update policy with given parameters.
*
* @param initialStepSize Initial Step Size.
* @param kappa Additive increase constant for step size.
* @param phi Multiplicative decrease factor for step size.
* @param theta Decay rate for the exponential moving average.
* @param minStepSize Minimum allowed step size for any parameter
* (default: 1e-8).
*/
DeltaBarDeltaUpdate(
const double initialStepSize,
const double kappa,
const double phi,
const double theta,
const double minStepSize = 1e-8) :
initialStepSize(initialStepSize),
kappa(kappa),
phi(phi),
theta(theta),
minStepSize(minStepSize)
{
/* Do nothing. */
}
//! Access the initialStepSize hyperparameter.
double InitialStepSize() const { return initialStepSize; }
//! Modify the initialStepSize hyperparameter.
double& InitialStepSize() { return initialStepSize; }
//! Access the kappa hyperparameter.
double Kappa() const { return kappa; }
//! Modify the kappa hyperparameter.
double& Kappa() { return kappa; }
//! Access the phi hyperparameter.
double Phi() const { return phi; }
//! Modify the phi hyperparameter.
double& Phi() { return phi; }
//! Access the theta hyperparameter.
double Theta() const { return theta; }
//! Modify the theta hyperparameter.
double& Theta() { return theta; }
//! Access the minStepSize hyperparameter.
double MinStepSize() const { return minStepSize; }
//! Modify the minStepSize hyperparameter.
double& MinStepSize() { return minStepSize; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template <typename MatType, typename GradType>
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This is called by the optimizer method before the start of the iteration
* update process.
*
* @param parent Instantiated parent class.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(
const DeltaBarDeltaUpdate& parent,
const size_t rows,
const size_t cols) :
parent(parent),
kappa(ElemType(parent.kappa)),
phi(ElemType(parent.phi)),
theta(ElemType(parent.theta)),
minStepSize(ElemType(parent.minStepSize))
{
deltaBar.zeros(rows, cols);
epsilon.set_size(rows, cols);
epsilon.fill(ElemType(parent.InitialStepSize()));
}
/**
* Update step for Gradient Descent.
*
* @param iterate Parameters that minimize the function.
* @param stepSize Step size to be used for the given iteration.
* @param delta The gradient matrix.
*/
void Update(MatType& iterate,
const double /* stepSize */,
const GradType& delta)
{
const MatType signMatrix = sign(delta % deltaBar);
epsilon += conv_to<MatType>::from((signMatrix == +1) * kappa -
(signMatrix == -1) * phi % epsilon);
epsilon.clamp(minStepSize, arma::Datum<ElemType>::inf);
deltaBar = theta * deltaBar + (1 - theta) * delta;
iterate -= epsilon % delta;
}
private:
//! The instantiated parent class.
const DeltaBarDeltaUpdate& parent;
//! The exponential average of past gradients.
MatType deltaBar;
//! Tracks the current step size for each parameter.
MatType epsilon;
// Parent parameters converted to the element type of the matrix.
ElemType kappa;
ElemType phi;
ElemType theta;
ElemType minStepSize;
};
private:
//! The initialStepSize hyperparameter.
double initialStepSize;
//! The kappa hyperparameter.
double kappa;
//! The phi hyperparameter.
double phi;
//! The theta hyperparameter.
double theta;
//! The minStepSize hyperparameter.
double minStepSize;
};
} // namespace ens
#endif // ENSMALLEN_DELTA_BAR_DELTA_UPDATE_HPP
@@ -0,0 +1,178 @@
/**
* @file momentum_delta_bar_delta_update.hpp
* @author Ranjodh Singh
*
* MomentumDeltaBarDelta update policy for Gradient Descent.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_UPDATE_HPP
#define ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_UPDATE_HPP
namespace ens {
/**
* MomentumDeltaBarDelta update policy for Gradient Descent.
*
* A DeltaBarDelta variant that incorporates the following modifications:
* - In the original DeltaBarDelta, the momentum term (delta_bar) is used
* solely for sign comparison with the current gradient and does not
* participate in the parameter update. In this modified variant, the
* momentum term (velocity) is directly used to update the parameters.
* - Instead of adjusting the step size directly, each parameter maintains
* a gain value initialized to 1.0. Updates apply additive increases or
* multiplicative decreases to this gain. The effective step size for a
* parameter is the product of its initial step size and its current gain.
*
* Note: This variant originates from optimization of the t-SNE cost function.
*
* @code
* @article{jacobs1988increased,
* title = {Increased Rates of Convergence Through Learning Rate
* Adaptation},
* author = {Jacobs, Robert A.},
* journal = {Neural Networks},
* volume = {1},
* number = {4},
* pages = {295--307},
* year = {1988},
* publisher = {Pergamon}
* }
* @endcode
*/
class MomentumDeltaBarDeltaUpdate
{
public:
/**
* Construct the MomentumDeltaBarDelta update policy with given parameters.
*
* @param kappa Additive increase constant for step size.
* @param phi Multiplicative decrease factor for step size.
* @param momentum The momentum decay hyperparameter.
* @param minGain Minimum allowed gain (scaling factor) for any parameter
* (default: 1e-8).
*/
MomentumDeltaBarDeltaUpdate(
const double kappa = 0.2,
const double phi = 0.8,
const double momentum = 0.5,
const double minGain = 1e-8) :
kappa(kappa),
phi(phi),
momentum(momentum),
minGain(minGain)
{
/* Do nothing. */
}
//! Access the kappa hyperparameter.
double Kappa() const { return kappa; }
//! Modify the kappa hyperparameter.
double& Kappa() { return kappa; }
//! Access the phi hyperparameter.
double Phi() const { return phi; }
//! Modify the phi hyperparameter.
double& Phi() { return phi; }
//! Access the momentum hyperparameter.
double Momentum() const { return momentum; }
//! Modify the momentum hyperparameter.
double& Momentum() { return momentum; }
//! Access the minGain hyperparameter.
double MinGain() const { return minGain; }
//! Modify the minGain hyperparameter.
double& MinGain() { return minGain; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template <typename MatType, typename GradType>
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This is called by the optimizer method before the start of the iteration
* update process.
*
* @param parent Instantiated parent class.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(
const MomentumDeltaBarDeltaUpdate& parent,
const size_t rows,
const size_t cols) :
parent(parent),
kappa(ElemType(parent.kappa)),
phi(ElemType(parent.phi)),
momentum(ElemType(parent.momentum)),
minGain(ElemType(parent.minGain))
{
gains.ones(rows, cols);
velocity.zeros(rows, cols);
}
/**
* Update step for Gradient Descent.
*
* @param iterate Parameters that minimize the function.
* @param stepSize Step size to be used for the given iteration.
* @param gradient The gradient matrix.
*/
void Update(MatType& iterate,
const double stepSize,
const GradType& gradient)
{
gains += conv_to<MatType>::from(
(sign(gradient) != sign(velocity)) * kappa -
(sign(gradient) == sign(velocity)) * (1 - phi) % gains);
gains.clamp(minGain, arma::Datum<ElemType>::inf);
velocity = momentum * velocity - (ElemType(stepSize) * gains) % gradient;
iterate += velocity;
}
private:
//! The instantiated parent class.
const MomentumDeltaBarDeltaUpdate& parent;
//! The gains matrix.
MatType gains;
//! The velocity matrix.
MatType velocity;
// Parent parameters converted to the element type of the matrix.
ElemType kappa;
ElemType phi;
ElemType momentum;
ElemType minGain;
};
private:
//! The kappa hyperparameter.
double kappa;
//! The phi hyperparameter.
double phi;
//! The momentum hyperparameter.
double momentum;
//! The minGain hyperparameter.
double minGain;
};
} // namespace ens
#endif // ENSMALLEN_MOMENTUM_DELTA_BAR_DELTA_UPDATE_HPP
@@ -0,0 +1,207 @@
/**
* @file demon_adam.hpp
* @author Marcus Edel
*
* Definition of DemonAdam.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_DEMON_ADAM_DEMON_ADAM_HPP
#define ENSMALLEN_DEMON_ADAM_DEMON_ADAM_HPP
#include "../sgd/sgd.hpp"
#include "../adam/adam_update.hpp"
#include "../adam/adamax_update.hpp"
#include "../adam/amsgrad_update.hpp"
#include "../adam/nadam_update.hpp"
#include "../adam/nadamax_update.hpp"
#include "../adam/optimisticadam_update.hpp"
#include "demon_adam_update.hpp"
namespace ens {
/**
* DemonAdam automatically decays momentum, motivated by decaying the total
* contribution of a gradient to all future updates.
*
* For more information, see the following.
*
* @code
* @misc{
* title = {Decaying momentum helps neural network training},
* author = {John Chen and Cameron Wolfe and Zhao Li
* and Anastasios Kyrillidis},
* url = {https://arxiv.org/abs/1910.04952}
* year = {2019}
* }
*
* DemonAdam can optimize differentiable separable functions. For more details,
* see the documentation on function types include with this distribution or on
* the ensmallen website.
*
* @tparam UpdateRule Adam optimizer update rule to be used.
*/
template<typename UpdateRule = AdamUpdate>
class DemonAdamType
{
public:
/**
* Construct the DemonAdam optimizer with the given function and parameters.
* The defaults here are not necessarily good for the given problem, so it is
* suggested that the values used be tailored to the task at hand. The
* maximum number of iterations refers to the maximum number of points that
* are processed (i.e., one iteration equals one point; one iteration does not
* equal one pass over the dataset).
*
* @param stepSize Step size for each iteration.
* @param batchSize Number of points to process in a single step.
* @param momentum The initial momentum coefficient.
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param beta1 Exponential decay rate for the first moment estimates.
* @param beta2 Exponential decay rate for the weighted infinity norm
* estimates.
* @param eps Value used to initialise the mean squared gradient parameter.
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param shuffle If true, the function order is shuffled; otherwise, each
* function is visited in linear order.
* @param resetPolicy If true, parameters are reset before every Optimize
* call; otherwise, their values are retained.
* @param exactObjective Calculate the exact objective (Default: estimate the
* final objective obtained on the last pass over the data).
*/
DemonAdamType(const double stepSize = 0.001,
const size_t batchSize = 32,
const double momentum = 0.9,
const double beta1 = 0.9,
const double beta2 = 0.999,
const double eps = 1e-8,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true,
const bool exactObjective = false) :
optimizer(stepSize,
batchSize,
maxIterations,
tolerance,
shuffle,
DemonAdamUpdate<UpdateRule>(maxIterations * batchSize,
momentum, UpdateRule(eps, beta1, beta2)),
NoDecay(),
resetPolicy,
exactObjective)
{ /* Nothing to do here. */ }
/**
* Optimize the given function using DemonAdam. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam SeparableFunctionType Type of the function to optimize.
* @tparam MatType Type of matrix to optimize with.
* @tparam GradType Type of matrix to use to represent function gradients.
* @tparam CallbackTypes Types of callback functions.
* @param function Function to optimize.
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.template Optimize<
SeparableFunctionType, MatType, GradType, CallbackTypes...>(
function, iterate, std::forward<CallbackTypes>(callbacks)...);
}
//! Forward the MatType as GradType.
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<SeparableFunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the step size.
double StepSize() const { return optimizer.StepSize(); }
//! Modify the step size.
double& StepSize() { return optimizer.StepSize(); }
//! Get the batch size.
size_t BatchSize() const { return optimizer.BatchSize(); }
//! Modify the batch size.
size_t& BatchSize() { return optimizer.BatchSize(); }
//! Get the moment coefficient.
double Momentum() const { return optimizer.UpdatePolicy().Momentum(); }
//! Modify the moment coefficient.
double& Momentum() { return optimizer.UpdatePolicy().Momentum(); }
//! Get the momentum iteration number.
size_t MomentumIterations() const
{ return optimizer.UpdatePolicy().MomentumIterations(); }
//! Modify the momentum iteration number.
size_t& MomentumIterations()
{ return optimizer.UpdatePolicy().MomentumIterations(); }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return optimizer.MaxIterations(); }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return optimizer.MaxIterations(); }
//! Get the tolerance for termination.
double Tolerance() const { return optimizer.Tolerance(); }
//! Modify the tolerance for termination.
double& Tolerance() { return optimizer.Tolerance(); }
//! Get whether or not the individual functions are shuffled.
bool Shuffle() const { return optimizer.Shuffle(); }
//! Modify whether or not the individual functions are shuffled.
bool& Shuffle() { return optimizer.Shuffle(); }
//! Get whether or not the actual objective is calculated.
bool ExactObjective() const { return optimizer.ExactObjective(); }
//! Modify whether or not the actual objective is calculated.
bool& ExactObjective() { return optimizer.ExactObjective(); }
//! Get whether or not the update policy parameters
//! are reset before Optimize call.
bool ResetPolicy() const { return optimizer.ResetPolicy(); }
//! Modify whether or not the update policy parameters
//! are reset before Optimize call.
bool& ResetPolicy() { return optimizer.ResetPolicy(); }
private:
//! The Stochastic Gradient Descent object with DemonAdam policy.
SGD<DemonAdamUpdate<UpdateRule>> optimizer;
};
using DemonAdam = DemonAdamType<AdamUpdate>;
using DemonAdaMax = DemonAdamType<AdaMaxUpdate>;
using DemonAMSGrad = DemonAdamType<AMSGradUpdate>;
using DemonNadam = DemonAdamType<NadamUpdate>;
using DemonNadaMax = DemonAdamType<NadaMaxUpdate>;
using DemonOptimisticAdam = DemonAdamType<OptimisticAdamUpdate>;
} // namespace ens
#endif
@@ -0,0 +1,174 @@
/**
* @file demon_sgd_update.hpp
* @author Marcus Edel
*
* Implementation of DemonAdam.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_DEMON_ADAM_DEMON_ADAM_UPDATE_HPP
#define ENSMALLEN_DEMON_ADAM_DEMON_ADAM_UPDATE_HPP
#include <assert.h>
namespace ens {
/**
* DemonAdam automatically decays momentum, motivated by decaying the total
* contribution of a gradient to all future updates.
*
* For more information, see the following.
*
* @code
* @misc{
* title = {Decaying momentum helps neural network training},
* author = {John Chen and Cameron Wolfe and Zhao Li
* and Anastasios Kyrillidis},
* url = {https://arxiv.org/abs/1910.04952}
* year = {2019}
* }
* @endcode
*
* @tparam UpdateRule DemonAdam optimizer update rule to be used.
*/
template<typename UpdateRule>
class DemonAdamUpdate
{
public:
/**
* Construct the DemonAdam update policy with the given parameters.
*
* @param momentumIterations The number of iterations before the momentum
* will decay to zero.
* @param momentum The initial momentum coefficient.
* @param adamUpdate Instantiated Adam update policy used to adjust the given
* parameters.
*/
DemonAdamUpdate(const size_t momentumIterations = 100,
const double momentum = 0.9,
const UpdateRule& adamUpdate = UpdateRule()) :
T(momentumIterations),
betaInit(momentum),
t(0),
adamUpdateInst(adamUpdate)
{
// Make sure the momentum iterations parameter is non-zero.
assert(momentumIterations != 0 && "The number of iterations before the "
"momentum will decay is zero, make sure the max iterations and "
"batch size parameter is set correctly. "
"Default: momentumIterations = maxIterations / batchSize.");
}
//! Get the momentum coefficient.
double Momentum() const { return betaInit; }
//! Modify the momentum coefficient.
double& Momentum() { return betaInit; }
//! Get the current iteration number.
size_t Iteration() const { return t; }
//! Modify the current iteration number.
size_t& Iteration() { return t; }
//! Get the momentum ion number.
size_t MomentumIterations() const { return T; }
//! Modify the momentum iteration number.
size_t& MomentumIterations() { return T; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
// Convenient typedef.
typedef typename UpdateRule::template Policy<MatType, GradType>
InstUpdateRuleType;
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent Instantiated PadamUpdate parent object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(DemonAdamUpdate& parent,
const size_t rows,
const size_t cols) :
parent(parent),
adamUpdate(new InstUpdateRuleType(parent.adamUpdateInst, rows, cols)),
betaInit(ElemType(parent.betaInit))
{ /* Nothing to do here */ }
/**
* Clean any memory associated with the Polciy object.
*/
~Policy()
{
delete adamUpdate;
}
/**
* Update step for DamonAdam.
*
* @param iterate Parameters that minimize the function.
* @param stepSize Step size to be used for the given iteration.
* @param gradient The gradient matrix.
*/
void Update(MatType& iterate,
const double stepSize,
const GradType& gradient)
{
ElemType decayRate = 1;
if (parent.t > 0)
decayRate = 1 - ElemType((double) parent.t / (double) parent.T);
const ElemType betaDecay = betaInit * decayRate;
const ElemType beta = betaDecay / ((1 - betaInit) + betaDecay);
// Perform the update.
iterate *= beta;
// Apply the adam update.
adamUpdate->Update(iterate, stepSize, gradient);
// Increment the iteration counter variable.
++parent.t;
}
private:
// Instantiated parent object.
DemonAdamUpdate<UpdateRule>& parent;
// The update policy.
InstUpdateRuleType* adamUpdate;
// Optimizer parameter converted to the element type of the optimization.
ElemType betaInit;
};
private:
//! The number of momentum iterations.
size_t T;
//! Initial momentum coefficient.
double betaInit;
//! The number of iterations.
size_t t;
//! The adam update policy.
UpdateRule adamUpdateInst;
};
} // namespace ens
#endif
@@ -0,0 +1,178 @@
/**
* @file demon_sgd.hpp
* @author Marcus Edel
*
* Definition of DemonSGD.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_DEMON_SGD_DEMON_SGD_HPP
#define ENSMALLEN_DEMON_SGD_DEMON_SGD_HPP
#include "../sgd/sgd.hpp"
#include "demon_sgd_update.hpp"
namespace ens {
/**
* DemonSGD automatically decays momentum, motivated by decaying the total
* contribution of a gradient to all future updates.
*
* For more information, see the following.
*
* @code
* @misc{
* title = {Decaying momentum helps neural network training},
* author = {John Chen and Cameron Wolfe and Zhao Li
* and Anastasios Kyrillidis},
* url = {https://arxiv.org/abs/1910.04952}
* year = {2019}
* }
*
* DemonSGD can optimize differentiable separable functions. For more details,
* see the documentation on function types include with this distribution or on
* the ensmallen website.
*/
class DemonSGD
{
public:
/**
* Construct the DemonSGD optimizer with the given function and parameters.
* The defaults here are not necessarily good for the given problem, so it is
* suggested that the values used be tailored to the task at hand. The
* maximum number of iterations refers to the maximum number of points that
* are processed (i.e., one iteration equals one point; one iteration does not
* equal one pass over the dataset).
*
* @param stepSize Step size for each iteration.
* @param batchSize Number of points to process in a single step.
* @param momentum The initial momentum coefficient.
* @param maxIterations Maximum number of iterations allowed (0 means no
* limit).
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param shuffle If true, the function order is shuffled; otherwise, each
* function is visited in linear order.
* @param resetPolicy If true, parameters are reset before every Optimize
* call; otherwise, their values are retained.
* @param exactObjective Calculate the exact objective (Default: estimate the
* final objective obtained on the last pass over the data).
*/
DemonSGD(const double stepSize = 0.001,
const size_t batchSize = 32,
const double momentum = 0.9,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true,
const bool exactObjective = false) :
optimizer(stepSize,
batchSize,
maxIterations,
tolerance,
shuffle,
DemonSGDUpdate(maxIterations * batchSize, momentum),
NoDecay(),
resetPolicy,
exactObjective)
{ /* Nothing to do here. */ }
/**
* Optimize the given function using DemonSGD. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam SeparableFunctionType Type of the function to optimize.
* @tparam MatType Type of matrix to optimize with.
* @tparam GradType Type of matrix to use to represent function gradients.
* @tparam CallbackTypes Types of callback functions.
* @param function Function to optimize.
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.template Optimize<
SeparableFunctionType, MatType, GradType, CallbackTypes...>(
function, iterate, std::forward<CallbackTypes>(callbacks)...);
}
//! Forward the MatType as GradType.
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<SeparableFunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the step size.
double StepSize() const { return optimizer.StepSize(); }
//! Modify the step size.
double& StepSize() { return optimizer.StepSize(); }
//! Get the batch size.
size_t BatchSize() const { return optimizer.BatchSize(); }
//! Modify the batch size.
size_t& BatchSize() { return optimizer.BatchSize(); }
//! Get the moment coefficient.
double Momentum() const { return optimizer.UpdatePolicy().Momentum(); }
//! Modify the moment coefficient.
double& Momentum() { return optimizer.UpdatePolicy().Momentum(); }
//! Get the momentum iteration number.
size_t MomentumIterations() const
{ return optimizer.UpdatePolicy().MomentumIterations(); }
//! Modify the momentum iteration number.
size_t& MomentumIterations()
{ return optimizer.UpdatePolicy().MomentumIterations(); }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return optimizer.MaxIterations(); }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return optimizer.MaxIterations(); }
//! Get the tolerance for termination.
double Tolerance() const { return optimizer.Tolerance(); }
//! Modify the tolerance for termination.
double& Tolerance() { return optimizer.Tolerance(); }
//! Get whether or not the individual functions are shuffled.
bool Shuffle() const { return optimizer.Shuffle(); }
//! Modify whether or not the individual functions are shuffled.
bool& Shuffle() { return optimizer.Shuffle(); }
//! Get whether or not the actual objective is calculated.
bool ExactObjective() const { return optimizer.ExactObjective(); }
//! Modify whether or not the actual objective is calculated.
bool& ExactObjective() { return optimizer.ExactObjective(); }
//! Get whether or not the update policy parameters
//! are reset before Optimize call.
bool ResetPolicy() const { return optimizer.ResetPolicy(); }
//! Modify whether or not the update policy parameters
//! are reset before Optimize call.
bool& ResetPolicy() { return optimizer.ResetPolicy(); }
private:
//! The Stochastic Gradient Descent object with DemonSGD policy.
SGD<DemonSGDUpdate> optimizer;
};
} // namespace ens
#endif
@@ -0,0 +1,145 @@
/**
* @file demon_sgd_update.hpp
* @author Marcus Edel
*
* Implementation of DemonSGD.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_DEMON_SGD_DEMON_SGD_UPDATE_HPP
#define ENSMALLEN_DEMON_SGD_DEMON_SGD_UPDATE_HPP
namespace ens {
/**
* DemonSGD automatically decays momentum, motivated by decaying the total
* contribution of a gradient to all future updates.
*
* For more information, see the following.
*
* @code
* @misc{
* title = {Decaying momentum helps neural network training},
* author = {John Chen and Cameron Wolfe and Zhao Li
* and Anastasios Kyrillidis},
* url = {https://arxiv.org/abs/1910.04952}
* year = {2019}
* }
* @endcode
*/
class DemonSGDUpdate
{
public:
/**
* Construct the DemonSGD update policy with the given parameters.
*
* @param momentumIterations The number of iterations before the momentum
* will decay to zero.
* @param momentum The initial momentum coefficient.
*/
DemonSGDUpdate(const size_t momentumIterations = 100,
const double momentum = 0.9) :
T(momentumIterations),
betaInit(momentum),
t(0)
{
// Make sure the momentum iterations parameter is non-zero.
assert(momentumIterations != 0 && "The number of iterations before the "
"momentum will decay is zero, make sure the max iterations and "
"batch size parameter is set correctly. "
"Default: momentumIterations = maxIterations * batchSize.");
}
//! Get the momentum coefficient.
double Momentum() const { return betaInit; }
//! Modify the momentum coefficient.
double& Momentum() { return betaInit; }
//! Get the current iteration number.
size_t Iteration() const { return t; }
//! Modify the current iteration number.
size_t& Iteration() { return t; }
//! Get the momentum iteration number.
size_t MomentumIterations() const { return T; }
//! Modify the momentum iteration number.
size_t& MomentumIterations() { return T; }
/**
* The UpdatePolicyType policy classes must contain an internal 'Policy'
* template class with two template arguments: MatType and GradType. This is
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
typedef typename MatType::elem_type ElemType;
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent Instantiated PadamUpdate parent object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(DemonSGDUpdate& parent,
const size_t /* rows */,
const size_t /* cols */) :
parent(parent),
betaInit(ElemType(parent.betaInit))
{ /* Nothing to do here */ }
/**
* Update step for DemonSGD.
*
* @param iterate Parameters that minimize the function.
* @param stepSize Step size to be used for the given iteration.
* @param gradient The gradient matrix.
*/
void Update(MatType& iterate,
const double stepSize,
const GradType& gradient)
{
ElemType decayRate = 1;
if (parent.t > 0)
decayRate = 1 - ElemType((double) parent.t / (double) parent.T);
const ElemType betaDecay = betaInit * decayRate;
const ElemType beta = betaDecay / ((1 - betaInit) + betaDecay);
// Perform the update.
iterate *= beta;
iterate -= ElemType(stepSize) * gradient;
// Increment the iteration counter variable.
++parent.t;
}
private:
// Instantiated parent object.
DemonSGDUpdate& parent;
// Optimizer parameter converted to the element type of the optimization.
ElemType betaInit;
};
private:
// The number of momentum iterations.
size_t T;
// Initial momentum coefficient.
double betaInit;
// The number of iterations.
size_t t;
};
} // namespace ens
#endif
+7 -7
View File
@@ -12,20 +12,20 @@
// This follows the Semantic Versioning pattern defined in https://semver.org/.
#define ENS_VERSION_MAJOR 2
#define ENS_VERSION_MAJOR 3
// The minor version is two digits so regular numerical comparisons of versions
// work right. The first minor version of a release is always 10.
#define ENS_VERSION_MINOR 16
#define ENS_VERSION_PATCH 2
#define ENS_VERSION_MINOR 11
#define ENS_VERSION_PATCH 0
// If this is a release candidate, it will be reflected in the version name
// (i.e. the version name will be "RC1", "RC2", etc.). Otherwise the version
// name will typically be a seemingly arbitrary set of words that does not
// contain the capitalized string "RC".
#define ENS_VERSION_NAME "Severely Dented Can Of Polyurethane"
#define ENS_VERSION_NAME "Sunny Day"
// Incorporate the date the version was released.
#define ENS_VERSION_YEAR "2021"
#define ENS_VERSION_MONTH "03"
#define ENS_VERSION_DAY "24"
#define ENS_VERSION_YEAR "2025"
#define ENS_VERSION_MONTH "12"
#define ENS_VERSION_DAY "15"
namespace ens {
+1 -1
View File
@@ -106,7 +106,7 @@ class Eve
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
+31 -18
View File
@@ -49,8 +49,8 @@ template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Eve::Optimize(SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
@@ -98,7 +98,7 @@ Eve::Optimize(SeparableFunctionType& function,
v.zeros();
// Now iterate!
terminate |= Callback::BeginOptimization(*this, f, iterate, callbacks...);
Callback::BeginOptimization(*this, f, iterate, callbacks...);
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
@@ -123,30 +123,40 @@ Eve::Optimize(SeparableFunctionType& function,
terminate |= Callback::EvaluateWithGradient(*this, f, iterate,
objective, gradient, callbacks...);
if (terminate)
break;
m *= beta1;
m += (1 - beta1) * gradient;
m *= ElemType(beta1);
m += (1 - ElemType(beta1)) * gradient;
v *= beta2;
v += (1 - beta2) * (gradient % gradient);
v *= ElemType(beta2);
v += (1 - ElemType(beta2)) * (gradient % gradient);
const double biasCorrection1 = 1.0 - std::pow(beta1, (double) (i + 1));
const double biasCorrection2 = 1.0 - std::pow(beta2, (double) (i + 1));
const ElemType biasCorrection1 =
1 - std::pow(ElemType(beta1), ElemType(i + 1));
const ElemType biasCorrection2 =
1 - std::pow(ElemType(beta2), ElemType(i + 1));
if (i > 0)
{
const ElemType d = std::abs(objective - lastObjective) /
(std::min(objective, lastObjective) + epsilon);
(std::min(objective, lastObjective) + ElemType(epsilon));
dt *= beta3;
dt += (1 - beta3) * std::min(std::max(d, ElemType(1.0 / clip)),
dt *= ElemType(beta3);
dt += (1 - ElemType(beta3)) * std::min(std::max(d, ElemType(1.0 / clip)),
ElemType(clip));
}
lastObjective = objective;
iterate -= stepSize / dt * (m / biasCorrection1) /
(arma::sqrt(v / biasCorrection2) + epsilon);
// TODO: remove in ensmallen 4.0.0.
#if defined(ENS_OLD_SEPARABLE_STEP_BEHAVIOR)
iterate -= ElemType(stepSize) / dt * (m / biasCorrection1) /
(sqrt(v / biasCorrection2) + ElemType(epsilon));
#else
iterate -= (ElemType(stepSize) / (dt * effectiveBatchSize)) *
(m / biasCorrection1) / (sqrt(v / biasCorrection2) + ElemType(epsilon));
#endif
terminate |= Callback::StepTaken(*this, f, iterate, callbacks...);
@@ -172,9 +182,7 @@ Eve::Optimize(SeparableFunctionType& function,
return overallObjective;
}
if (std::abs(lastOverallObjective - overallObjective) < tolerance ||
Callback::BeginEpoch(*this, f, iterate, epoch, overallObjective,
callbacks...))
if (std::abs(lastOverallObjective - overallObjective) < tolerance)
{
Info << "Eve: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
@@ -183,6 +191,9 @@ Eve::Optimize(SeparableFunctionType& function,
return overallObjective;
}
terminate |= Callback::BeginEpoch(*this, f, iterate, epoch,
overallObjective, callbacks...);
// Reset the counter variables.
lastOverallObjective = overallObjective;
overallObjective = 0;
@@ -206,7 +217,9 @@ Eve::Optimize(SeparableFunctionType& function,
const ElemType objective = f.Evaluate(iterate, i, effectiveBatchSize);
overallObjective += objective;
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
// The optimization is finished, so we don't need to care about the result
// of the callback.
(void) Callback::Evaluate(*this, f, iterate, objective, callbacks...);
}
}
+220
View File
@@ -0,0 +1,220 @@
/**
* @file fasta.hpp
* @author Ryan Curtin
*
* An implementation of FASTA (Fast Adaptive Shrinkage/Thresholding Algorithm).
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_FASTA_FASTA_HPP
#define ENSMALLEN_FASTA_FASTA_HPP
#include "../fbs/l1_penalty.hpp"
#include "../fbs/l1_constraint.hpp"
namespace ens {
/**
* FASTA (Fast Adaptive Shrinkage/Thresholding Algorithm) is a proximal
* gradient optimization technique for optimizing a function of the form
*
* h(x) = f(x) + g(x)
*
* where f(x) is a differentiable function and g(x) is an arbitrary
* non-differentiable function. In such a situation, standard gradient descent
* techniques cannot work because of the non-differentiability of g(x). To work
* around this, FASTA takes a _forward step_ that is just a gradient descent
* step on f(x), and then a _backward step_ that is the _proximal operator_
* corresponding to g(x). This continues until convergence.
*
* This implementation of FASTA allows specification of the backward step (or
* proximal operator) via the `BackwardStepType` template parameter. When using
* FBS, the differentiable `FunctionType` given to `Optimize()` should be f(x),
* *not* the combined function h(x). g(x) should be specified by the choice of
* `BackwardStepType` (e.g. `L1Penalty` or `L1Maximum`). The `Optimize()`
* function will then return optimized coordinates for h(x), not f(x).
*
* For more information, see the following paper:
*
* ```
* @article{goldstein2014field,
* title={A field guide to forward-backward splitting with a FASTA
* implementation},
* author={Goldstein, Tom and Studer, Christoph and Baraniuk, Richard},
* journal={arXiv preprint arXiv:1411.3406},
* year={2014}
* }
* ```
*/
template<typename BackwardStepType = L1Penalty>
class FASTA
{
public:
/**
* Construct the FASTA optimizer with the given options, using a
* default-constructed BackwardStepType.
*/
FASTA(const size_t maxIterations = 10000,
const double tolerance = 1e-7,
const size_t maxLineSearchSteps = 50,
const double stepSizeAdjustment = 2.0,
const size_t lineSearchLookback = 10,
const bool estimateStepSize = true,
const size_t estimateTrials = 10,
const double maxStepSize = 0.001);
/**
* Construct the FASTA optimizer with the given options.
*/
FASTA(BackwardStepType backwardStepType,
const size_t maxIterations = 10000,
const double tolerance = 1e-7,
const size_t maxLineSearchSteps = 50,
const double stepSizeAdjustment = 2.0,
const size_t lineSearchLookback = 10,
const bool estimateStepSize = true,
const size_t estimateTrials = 10,
const double maxStepSize = 0.001);
/**
* Optimize the given function using FASTA. The given starting
* point will be modified to store the finishing point of the algorithm,
* the final objective value is returned.
*
* The FunctionType template class must provide the following functions:
*
* double Evaluate(const arma::mat& coordinates);
* void Gradient(const arma::mat& coordinates,
* arma::mat& gradient);
*
* @tparam FunctionType Type of function to be optimized.
* @tparam MatType Type of objective matrix.
* @tparam GradType Type of gradient matrix (default is MatType).
* @tparam CallbackTypes Types of callback functions.
* @param function Function to be optimized.
* @param iterate Input with starting point, and will be modified to save
* the output optimial solution coordinates.
* @param callbacks Callback functions.
* @return Objective value at the final solution.
*/
template<typename FunctionType, typename MatType, typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(FunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename FunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(FunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<FunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the backward step object.
const BackwardStepType& BackwardStep() const { return backwardStep; }
//! Modify the backward step object.
BackwardStepType& BackwardStep() { return backwardStep; }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return maxIterations; }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return maxIterations; }
//! Get the tolerance on the gradient norm for termination.
double Tolerance() const { return tolerance; }
//! Modify the tolerance on the gradient norm for termination.
double& Tolerance() { return tolerance; }
//! Get the maximum number of line search steps.
size_t MaxLineSearchSteps() const { return maxLineSearchSteps; }
//! Modify the maximum number of line search steps.
size_t& MaxLineSearchSteps() { return maxLineSearchSteps; }
//! Get the step size adjustment parameter.
double StepSizeAdjustment() const { return stepSizeAdjustment; }
//! Modify the step size adjustment parameter.
double& StepSizeAdjustment() { return stepSizeAdjustment; }
//! Get the maximum number of iterations to look back during a line search.
size_t LineSearchLookback() const { return lineSearchLookback; }
//! Modify the maximum number of iterations to look back during a line search.
size_t& LineSearchLookback() { return lineSearchLookback; }
//! Get whether or not to estimate the initial step size.
bool EstimateStepSize() const { return estimateStepSize; }
//! Modify whether or not to estimate the initial step size.
bool& EstimateStepSize() { return estimateStepSize; }
//! Get the number of trials to use for Lipschitz constant estimation.
size_t EstimateTrials() const { return estimateTrials; }
//! Modify the number of trials to use for Lipschitz constant estimation.
size_t& EstimateTrials() { return estimateTrials; }
//! Get the maximum step size. If Optimize() has been called, this will
//! contain the estimated maximum step size value.
double MaxStepSize() const { return maxStepSize; }
//! Modify the step size (ignored if EstimateStepSize() is true).
double& MaxStepSize() { return maxStepSize; }
private:
//! Utility function: fill with random values.
template<typename MatType>
static void RandomFill(MatType& x,
const size_t rows,
const size_t cols,
const typename MatType::elem_type maxVal);
template<typename eT>
static void RandomFill(arma::SpMat<eT>& x,
const size_t rows,
const size_t cols,
const eT maxVal);
template<typename FunctionType, typename MatType>
void EstimateLipschitzStepSize(FunctionType& f, const MatType& x);
//! The instantiated backward step object.
BackwardStepType backwardStep;
//! The maximum number of allowed iterations.
size_t maxIterations;
//! The tolerance for termination.
double tolerance;
//! The maximum number of line search trials.
size_t maxLineSearchSteps;
//! The step size adjustment parameter for the line search.
double stepSizeAdjustment;
//! The maximum number of iterations to look back during a line search.
size_t lineSearchLookback;
//! Whether or not to try and estimate the initial step size.
bool estimateStepSize;
//! Number of trials to use for initial step size estimation.
size_t estimateTrials;
//! The maximum step size to use (estimated if estimateStepSize is true).
double maxStepSize;
};
} // namespace ens
// Include implementation.
#include "fasta_impl.hpp"
#endif
+549
View File
@@ -0,0 +1,549 @@
/**
* @file fasta_impl.hpp
* @author Ryan Curtin
*
* Implementation of FASTA (Fast Adaptive Shrinkage/Thresholding Algorithm).
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_FASTA_FASTA_IMPL_HPP
#define ENSMALLEN_FASTA_FASTA_IMPL_HPP
// In case it hasn't been included yet.
#include "fasta.hpp"
#include <ensmallen_bits/function.hpp>
namespace ens {
//! Constructor of the FBS class.
template<typename BackwardStepType>
FASTA<BackwardStepType>::FASTA(const size_t maxIterations,
const double tolerance,
const size_t maxLineSearchSteps,
const double stepSizeAdjustment,
const size_t lineSearchLookback,
const bool estimateStepSize,
const size_t estimateTrials,
const double maxStepSize) :
maxIterations(maxIterations),
tolerance(tolerance),
maxLineSearchSteps(maxLineSearchSteps),
stepSizeAdjustment(stepSizeAdjustment),
lineSearchLookback(lineSearchLookback),
estimateStepSize(estimateStepSize),
estimateTrials(estimateTrials),
maxStepSize(maxStepSize)
{
// Check estimateSteps parameter.
if (estimateStepSize && estimateTrials == 0)
{
throw std::invalid_argument("FASTA::FASTA(): estimateTrials must be greater"
" than 0!");
}
if (lineSearchLookback == 0)
{
throw std::invalid_argument("FASTA::FASTA(): lineSearchLookback cannot be "
"0!");
}
}
template<typename BackwardStepType>
FASTA<BackwardStepType>::FASTA(BackwardStepType backwardStep,
const size_t maxIterations,
const double tolerance,
const size_t maxLineSearchSteps,
const double stepSizeAdjustment,
const size_t lineSearchLookback,
const bool estimateStepSize,
const size_t estimateTrials,
const double maxStepSize) :
backwardStep(std::move(backwardStep)),
maxIterations(maxIterations),
tolerance(tolerance),
maxLineSearchSteps(maxLineSearchSteps),
stepSizeAdjustment(stepSizeAdjustment),
lineSearchLookback(lineSearchLookback),
estimateStepSize(estimateStepSize),
estimateTrials(estimateTrials),
maxStepSize(maxStepSize)
{
// Check estimateSteps parameter.
if (estimateStepSize && estimateTrials == 0)
{
throw std::invalid_argument("FASTA::FASTA(): estimateTrials must be greater"
" than 0!");
}
if (lineSearchLookback == 0)
{
throw std::invalid_argument("FASTA::FASTA(): lineSearchLookback cannot be "
"0!");
}
}
//! Optimize the function (minimize).
template<typename BackwardStepType>
template<typename FunctionType, typename MatType, typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
FASTA<BackwardStepType>::Optimize(FunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
typedef Function<FunctionType, BaseMatType, BaseGradType> FullFunctionType;
FullFunctionType& f = static_cast<FullFunctionType&>(function);
// Make sure we have all necessary functions.
traits::CheckFunctionTypeAPI<FullFunctionType, BaseMatType, BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
// Sanity check: make sure lineSearchLookback is valid.
if (lineSearchLookback == 0)
{
throw std::invalid_argument("FASTA::FASTA(): lineSearchLookback cannot be "
"0!");
}
// Here we make a copy because we will use std::move() internally, and if
// iterateIn is an alias, this is unsafe. We will copy the final result back
// to iterateIn at the end.
BaseMatType x(iterateIn);
// To keep track of the function value.
ElemType currentFObj = f.Evaluate(x);
ElemType currentGObj = backwardStep.Evaluate(x);
ElemType currentObj = currentFObj + currentGObj;
// This will be the denominator of the normalized residual termination
// condition.
ElemType firstResidual = ElemType(0);
// This will be used in the non-monotone line search, to track the last
// several function values.
arma::Col<ElemType> lastFObjs(lineSearchLookback);
lastFObjs.fill(std::numeric_limits<ElemType>::min());
size_t currentObjPos = 0;
BaseGradType g(x.n_rows, x.n_cols);
BaseMatType lastXHat; // Used for residual checks.
BaseMatType lastX; // Used for residual and alpha reset checks.
BaseMatType xHat; // Used for residual checks.
BaseMatType lpaX = x; // Used for alpha reset check.
ElemType alpha = ElemType(1); // Initialize alpha^1 = 1.
ElemType lastAlpha = alpha;
// Controls early termination of the optimization process.
bool terminate = false;
// First, estimate the Lipschitz constant to set the initial/maximum step
// size, if the user asked us to.
if (estimateStepSize)
EstimateLipschitzStepSize(f, x);
// Keep track of the last step size we used.
ElemType currentStepSize = (ElemType) maxStepSize;
ElemType lastStepSize = (ElemType) maxStepSize;
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
Callback::BeginOptimization(*this, f, x, callbacks...);
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
{
// During this optimization, we want to optimize h(x) = f(x) + g(x).
// f(x) is `f`, but g(x) is specified by `BackwardStepType`.
// The first step is to compute a step size via a non-monotone line search.
// To do this, we need to compute the gradient f'(y) as required by the line
// search condition in Eq. (38). Note that our code does a little sleight
// of hand, and so `x` stores what the paper calls `y^k` here. (See the
// code for the adaptive step below.)
currentFObj = f.EvaluateWithGradient(x, g);
terminate |= Callback::EvaluateWithGradient(*this, f, x, currentFObj, g,
callbacks...);
// Use backtracking non-monotone line search to find the best step size.
// This is the version from the FASTA paper, but with a minor modification:
// we start our search at the last step size, and allow the search to
// increase the step size up to the maximum step size if it can. This is a
// more effective heuristic than simply starting at the largest allowable
// step size and shrinking from there, especially in regions where the
// gradient norm is small. It is also more effective than simply starting
// at the last step size and shrinking from there, as it prevents getting
// "stuck" with a very small step size.
bool lsDone = false;
size_t lsTrial = 0;
bool increasing = false; // Will be set during the first iteration.
ElemType lastFObj = ElemType(0);
BaseMatType lsLastX; // Only used in increasing mode.
BaseMatType lsLastXHat; // Only used in increasing mode.
BaseMatType xDiff;
lastX = std::move(x);
lastStepSize = currentStepSize;
currentStepSize = std::min(currentStepSize, (ElemType) maxStepSize);
// Ensure that the last `lineSearchLookback` objective values are recorded
// properly.
lastFObjs[currentObjPos] = currentFObj;
currentObjPos = (currentObjPos + 1) % lineSearchLookback;
const ElemType strictMaxFObj = currentFObj;
const ElemType maxFObj = lastFObjs.max();
while (!lsDone && !terminate)
{
if (lsTrial == maxLineSearchSteps)
{
if (increasing)
{
Warn << "FASTA::Optimize(): line search reached maximum number of "
<< "steps (" << maxLineSearchSteps << "); using step size "
<< currentStepSize << "." << std::endl;
break; // The step size is still valid.
}
else
{
Warn << "FASTA::Optimize(): could not find valid step size in range "
<< "(0, " << maxStepSize << "]! Terminating optimization."
<< std::endl;
terminate = true;
break;
}
}
// If the step size has converged to zero, we are done.
if (currentStepSize == ElemType(0))
{
Warn << "FASTA::Optimize(): computed zero step size; terminating "
<< "optimization." << std::endl;
terminate = true;
break;
}
// Perform forward update into x.
xHat = lastX - currentStepSize * g;
// (We must store xHat separately for the residual, so this copy is
// necessary.)
x = xHat;
backwardStep.ProximalStep(x, currentStepSize);
// Compute objective of new point.
const ElemType fObj = f.Evaluate(x);
terminate |= Callback::Evaluate(*this, f, x, fObj, callbacks...);
// Compute the quadratic approximation of the objective (the condition in
// Eq. (38)).
xDiff = (x - lastX);
// Note: since we allow the step size to increase, we have to modify the
// non-monotone line search a little bit to keep things from diverging.
// Specifically, if we are increasing the step size, then we force a
// monotone line search (by looking only at the previous function value).
// It is only when we are decreasing the step size that we allow
// relaxation.
const ElemType relaxedCond = maxFObj + dot(xDiff, g) +
(1 / (2 * currentStepSize)) * dot(xDiff, xDiff);
const ElemType strictCond = strictMaxFObj + dot(xDiff, g) +
(1 / (2 * currentStepSize)) * dot(xDiff, xDiff);
// If we're on the first iteration, we don't know if we should be
// searching for a step size by increasing or decreasing the step size.
// (Remember that our valid ranges of step sizes are [0, maxStepSize], and
// we are starting at lastStepSize.)
//
// Thus, if the condition is satisfied, let's try increasing the step size
// until it's no longer satisfied. Otherwise, we will have to decrease
// the step size.
if (lsTrial == 0)
{
increasing = ((fObj <= strictCond) && (std::isfinite(fObj)));
}
if (increasing)
{
// If we are in "increasing" mode, then termination occurs on the first
// iteration when the strict condition is *not* satisfied (and we use
// the last step size).
if ((fObj > strictCond) || (!std::isfinite(fObj)))
{
lsDone = true;
x = std::move(lsLastX);
xHat = std::move(lsLastXHat);
currentFObj = lastFObj;
currentStepSize = lastStepSize; // Take one step backwards.
}
else if (currentStepSize == (ElemType) maxStepSize)
{
// The condition is still satisfied, but the step size will be too big
// if we take another step. Go back to the maximum step size.
lsDone = true;
currentFObj = fObj;
}
else
{
// The condition is still satisfied; increase the step size.
lastStepSize = currentStepSize;
currentStepSize *= ElemType(stepSizeAdjustment);
lsLastX = std::move(x);
lsLastXHat = std::move(xHat);
lastFObj = fObj;
++lsTrial;
}
}
else
{
// If we are in "decreasing" mode, then termination occurs on the first
// iteration when the relaxed condition is satisfied.
if ((fObj <= relaxedCond) && (std::isfinite(fObj)))
{
lsDone = true;
currentFObj = fObj;
}
else
{
// The condition is not yet satisfied; decrease the step size.
currentStepSize /= ElemType(stepSizeAdjustment);
++lsTrial;
}
}
}
if (!lsDone)
{
// The line search failed, so terminate.
Warn << "FASTA::Optimize(): non-monotone line search failed after "
<< maxLineSearchSteps << " steps; terminating optimization."
<< std::endl;
x = std::move(lastX);
terminate = true;
}
// If we terminated during the line search, we are done.
if (terminate)
break;
// Now that we have taken a step, compute the full objective by computing
// g(x).
currentGObj = backwardStep.Evaluate(x);
currentObj = currentFObj + currentGObj;
// Output current objective function.
Info << "FASTA::Optimize(): iteration " << i << ", combined objective "
<< currentObj << " (f(x) = " << currentFObj << ", g(x) = "
<< currentGObj << "), step size " << currentStepSize << "."
<< std::endl;
// Sanity check for divergence.
if ((i > 1) && !std::isfinite(currentObj))
{
Warn << "FASTA::Optimize(): objective diverged to "
<< currentObj << "; terminating optimization." << std::endl;
terminate = true;
break;
}
// Now, check for convergence. The FASTA convergence check uses both the
// normalized residual and the relative residual, stopping when either
// becomes sufficiently small. The check depends on x before and after the
// proximal step.
// Compute residual. This is Eq. (40) in the paper.
const ElemType residual = norm(g + (xHat - x) / currentStepSize, 2);
// If this is the first iteration, store the residual as the first residual.
if (i == 1)
firstResidual = residual;
// First, check the normalized residual for convergence. This is Eq. (43)
// in the paper.
const ElemType eps = 20 * std::numeric_limits<ElemType>::epsilon();
const ElemType normalizedResidual = residual / (firstResidual + eps);
if ((i < 10) && (normalizedResidual < ElemType(1e-5)))
{
// Heuristic: sometimes the optimization starts in such an awful place
// that we are able to make huge amounts of progress in the first few
// iterations. In this case, reset the firstResidual to the slightly
// better point we get to by the tenth iterate.
firstResidual = residual;
}
else if ((i > 10) && (normalizedResidual < tolerance))
{
Info << "FASTA::Optimize(): normalized residual minimized within "
<< "tolerance " << tolerance << "; terminating optimization."
<< std::endl;
break;
}
// Next, check the relative residual for convergence. This is Eq. (42) in
// the paper.
const ElemType gNorm = norm(g, 2);
const ElemType proxStepNorm = norm((xHat - x) / currentStepSize, 2);
const ElemType relativeResidual = residual /
(std::max(gNorm, proxStepNorm) + 20 * eps);
if (relativeResidual < tolerance)
{
Info << "FASTA::Optimize(): relative residual minimized within "
<< "tolerance " << tolerance << "; terminating optimization."
<< std::endl;
break;
}
// Compute updated prediction parameter alpha.
lastAlpha = alpha;
alpha = (1 + std::sqrt(1 + 4 * std::pow(alpha, ElemType(2)))) / 2;
// Take a predictive step.
BaseMatType y = x + ((lastAlpha - 1) / alpha) * (x - lpaX);
// Sometimes alpha can get to be too large; this restart scheme is taken
// originally from O'Donoghue and Candes, "Adaptive restart for accelerated
// gradient schemes", 2012.
//
// The notation is confusing here when compared with Eq. (37) in the paper.
// This is because the paper is poorly notated, although it's not clear much
// has been done here to improve things. To translate:
//
// Paper Code Explanation
//
// y^k lastX This is the result of the predictive step on the
// previous iteration. In our code, we apply the
// predictive step to x, which next iteration becomes
// lastX.
//
// x^k x This is the iterate before the predictive step, this
// iteration.
//
// x^k-1 lpaX "Last Pre-Accelerated X"---we have to take a specific
// step to store this.
//
const ElemType restartCheck = dot(lastX - x, x - lpaX);
if (restartCheck > 0)
{
Info << "FASTA::Optimize(): alpha too large (" << alpha << "); reset to "
<< "1." << std::endl;
alpha = ElemType(1);
lastAlpha = ElemType(1);
}
lpaX = std::move(x);
x = std::move(y);
terminate |= Callback::StepTaken(*this, f, x, callbacks...);
}
if (!terminate)
{
Info << "FASTA::Optimize(): maximum iterations (" << maxIterations
<< ") reached; terminating optimization." << std::endl;
}
Callback::EndOptimization(*this, f, x, callbacks...);
((BaseMatType&) iterateIn) = x;
return currentObj;
} // Optimize()
template<typename BackwardStepType>
template<typename MatType>
void FASTA<BackwardStepType>::RandomFill(
MatType& x,
const size_t rows,
const size_t cols,
const typename MatType::elem_type maxVal)
{
x.randu(rows, cols);
x *= maxVal;
}
template<typename BackwardStepType>
template<typename eT>
void FASTA<BackwardStepType>::RandomFill(
arma::SpMat<eT>& x,
const size_t rows,
const size_t cols,
const eT maxVal)
{
eT density = eT(0.1);
// Try and keep the matrix from having too many elements.
if (rows * cols > 100000)
density = eT(0.01);
else if (rows * cols > 1000000)
density = eT(0.001);
else if (rows * cols > 10000000)
density = eT(0.0001);
x.sprandu(rows, cols, density);
// Make sure we got at least some nonzero elements...
while (x.n_nonzero == 0)
{
if (x.n_elem < 10)
x.sprandu(rows, cols, 1.0);
else
x.sprandu(rows, cols, 0.5);
}
x *= maxVal;
}
template<typename BackwardStepType>
template<typename FunctionType, typename MatType>
void FASTA<BackwardStepType>::EstimateLipschitzStepSize(
FunctionType& f,
const MatType& x)
{
typedef typename MatType::elem_type ElemType;
// Sanity check for estimateSteps parameter.
if (estimateTrials == 0)
{
throw std::invalid_argument("FASTA::Optimize(): estimateTrials must be "
"greater than 0!");
}
const ElemType xMax = std::max(ElemType(1), 2 * x.max());
ElemType sum = ElemType(0);
MatType x1, x2, gx1, gx2;
for (size_t t = 0; t < estimateTrials; ++t)
{
RandomFill(x1, x.n_rows, x.n_cols, xMax);
RandomFill(x2, x.n_rows, x.n_cols, xMax);
f.Gradient(x1, gx1);
f.Gradient(x2, gx2);
// Compute a Lipschitz constant estimate.
const ElemType lEst = norm(gx1 - gx2, 2) / norm(x1 - x2, 2);
sum += lEst;
}
sum /= estimateTrials;
if (sum == 0)
maxStepSize = std::numeric_limits<ElemType>::max();
else
maxStepSize = (10 / sum);
Info << "FASTA::Optimize(): estimated a maximum step size of "
<< maxStepSize << "." << std::endl;
}
} // namespace ens
#endif
+153
View File
@@ -0,0 +1,153 @@
/**
* @file fbs.hpp
* @author Ryan Curtin
*
* An implementation of Forward-Backward Splitting (FBS).
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_FBS_FBS_HPP
#define ENSMALLEN_FBS_FBS_HPP
#include "l1_penalty.hpp"
#include "l1_constraint.hpp"
namespace ens {
/**
* Forward-Backward Splitting is a proximal gradient optimization technique for
* optimizing a function of the form
*
* h(x) = f(x) + g(x)
*
* where f(x) is a differentiable function and g(x) is an arbitrary
* non-differentiable function. In such a situation, standard gradient descent
* techniques cannot work because of the non-differentiability of g(x). To work
* around this, FBS takes a _forward step_ that is just a gradient descent step
* on f(x), and then a _backward step_ that is the _proximal operator_
* corresponding to g(x). This continues until convergence.
*
* This implementation of FBS allows specification of the backward step (or
* proximal operator) via the `BackwardStepType` template parameter. When using
* FBS, the differentiable `FunctionType` given to `Optimize()` should be f(x),
* *not* the combined function h(x). g(x) should be specified by the choice of
* `BackwardStepType` (e.g. `L1Penalty` or `L1Maximum`). The `Optimize()`
* function will then return optimized coordinates for h(x), not f(x).
*
* For more information, see the following paper:
*
* ```
* @article{goldstein2014field,
* title={A field guide to forward-backward splitting with a FASTA
* implementation},
* author={Goldstein, Tom and Studer, Christoph and Baraniuk, Richard},
* journal={arXiv preprint arXiv:1411.3406},
* year={2014}
* }
* ```
*/
template<typename BackwardStepType = L1Penalty>
class FBS
{
public:
/**
* Construct the FBS optimizer with the given options, using a
* default-constructed BackwardStepType.
*/
FBS(const double stepSize = 0.001,
const size_t maxIterations = 10000,
const double tolerance = 1e-10);
/**
* Construct the FBS optimizer with the given options.
*/
FBS(BackwardStepType backwardStepType,
const double stepSize = 0.001,
const size_t maxIterations = 10000,
const double tolerance = 1e-10);
/**
* Optimize the given function using FBS. The given starting
* point will be modified to store the finishing point of the algorithm,
* the final objective value is returned.
*
* FunctionType template class must provide the following functions:
*
* double Evaluate(const arma::mat& coordinates);
* void Gradient(const arma::mat& coordinates,
* arma::mat& gradient);
*
* @tparam FunctionType Type of function to be optimized.
* @tparam MatType Type of objective matrix.
* @tparam GradType Type of gradient matrix (default is MatType).
* @tparam CallbackTypes Types of callback functions.
* @param function Function to be optimized.
* @param iterate Input with starting point, and will be modified to save
* the output optimial solution coordinates.
* @param callbacks Callback functions.
* @return Objective value at the final solution.
*/
template<typename FunctionType, typename MatType, typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
Optimize(FunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename FunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(FunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<FunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the backward step object.
const BackwardStepType& BackwardStep() const { return backwardStep; }
//! Modify the backward step object.
BackwardStepType& BackwardStep() { return backwardStep; }
//! Get the step size.
double StepSize() const { return stepSize; }
//! Modify the step size.
double& StepSize() { return stepSize; }
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return maxIterations; }
//! Modify the maximum number of iterations (0 indicates no limit).
size_t& MaxIterations() { return maxIterations; }
//! Get the tolerance for termination.
double Tolerance() const { return tolerance; }
//! Modify the tolerance for termination.
double& Tolerance() { return tolerance; }
private:
//! The instantiated backward step object.
BackwardStepType backwardStep;
//! The step size for FBS steps.
double stepSize;
//! The maximum number of allowed iterations.
size_t maxIterations;
//! The tolerance for termination.
double tolerance;
};
} // namespace ens
// Include implementation.
#include "fbs_impl.hpp"
#endif
+144
View File
@@ -0,0 +1,144 @@
/**
* @file fbs_impl.hpp
* @author Ryan Curtin
*
* Implementation of Forward-Backward Splitting (FBS).
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_FBS_FBS_IMPL_HPP
#define ENSMALLEN_FBS_FBS_IMPL_HPP
// In case it hasn't been included yet.
#include "fbs.hpp"
#include <ensmallen_bits/function.hpp>
namespace ens {
//! Constructor of the FBS class.
template<typename BackwardStepType>
FBS<BackwardStepType>::FBS(const double stepSize,
const size_t maxIterations,
const double tolerance) :
stepSize(stepSize),
maxIterations(maxIterations),
tolerance(tolerance)
{ /* Nothing to do. */ }
template<typename BackwardStepType>
FBS<BackwardStepType>::FBS(BackwardStepType backwardStep,
const double stepSize,
const size_t maxIterations,
const double tolerance) :
backwardStep(std::move(backwardStep)),
stepSize(stepSize),
maxIterations(maxIterations),
tolerance(tolerance)
{ /* Nothing to do. */ }
//! Optimize the function (minimize).
template<typename BackwardStepType>
template<typename FunctionType, typename MatType, typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsMatrixType<GradType>::value,
typename MatType::elem_type>::type
FBS<BackwardStepType>::Optimize(FunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
typedef Function<FunctionType, BaseMatType, BaseGradType> FullFunctionType;
FullFunctionType& f = static_cast<FullFunctionType&>(function);
// Make sure we have all necessary functions.
traits::CheckFunctionTypeAPI<FullFunctionType, BaseMatType, BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
BaseMatType& iterate = (BaseMatType&) iterateIn;
// To keep track of the function value.
ElemType currentObjective = std::numeric_limits<ElemType>::max();
ElemType currentFObjective = currentObjective;
ElemType currentGObjective = currentObjective;
ElemType lastObjective = currentObjective;
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
// Controls early termination of the optimization process.
bool terminate = false;
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
Callback::BeginOptimization(*this, f, iterate, callbacks...);
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
{
// During this optimization, we want to optimize h(x) = f(x) + g(x).
// f(x) is `f`, but g(x) is specified by `BackwardStepType`.
// First compute f(x) and f'(x).
currentFObjective = f.EvaluateWithGradient(iterate, gradient);
// Now compute g(x) to get the full objective.
currentGObjective = backwardStep.Evaluate(iterate);
lastObjective = currentObjective;
currentObjective = currentFObjective + currentGObjective;
terminate |= Callback::EvaluateWithGradient(*this, f, iterate,
currentObjective, gradient, callbacks...);
// Output current objective function.
Info << "FBS::Optimize(): iteration " << i << ", combined objective "
<< currentObjective << " (f(x) = " << currentFObjective << ", g(x) = "
<< currentGObjective << ")." << std::endl;
// Check for convergence.
if ((i > 1) && (std::abs(currentObjective - lastObjective) < tolerance))
{
Info << "FBS::Optimize(): minimized within objective tolerance "
<< tolerance << "; terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return currentObjective;
}
if ((i > 1) && !std::isfinite(currentObjective))
{
Warn << "FBS::Optimize(): objective diverged to " << currentObjective
<< "; terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return currentObjective;
}
// Perform forward update.
iterate -= ElemType(stepSize) * gradient;
// Now perform backward step (proximal update).
backwardStep.ProximalStep(iterate, stepSize);
terminate |= Callback::StepTaken(*this, f, iterate, callbacks...);
}
if (!terminate)
{
Info << "FBS::Optimize(): maximum iterations (" << maxIterations
<< ") reached; terminating optimization." << std::endl;
}
Callback::EndOptimization(*this, f, iterate, callbacks...);
return currentObjective;
} // Optimize()
} // namespace ens
#endif
@@ -0,0 +1,81 @@
/**
* @file l1_constraint.hpp
* @author Ryan Curtin
*
* An implementation of the proximal operator for the L1 constraint.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_FBS_L1_CONSTRAINT_HPP
#define ENSMALLEN_FBS_L1_CONSTRAINT_HPP
namespace ens {
/**
* The L1Constraint applies a specific constraint that the L1 norm of the
* parameters must be less than or equal to the given lambda value.
*
* Implementationally, this means that the proximal step is a projection onto
* the L1 ball of radius lambda. If the constraint is satisfied, `Evaluate()`
* will return 0. Otherwise, it will return infinity.
*
* This class is meant to be used with the FBS optimizer, and any other
* optimizer that uses a proximal operator/step.
*/
class L1Constraint
{
public:
/**
* Construct an L1Constraint with the given maximum L1 norm for the
* coordinates (lambda).
*/
L1Constraint(const double lambda = 0.0);
/**
* If the L1 norm of the coordinates is less than or equal to lambda, this
* returns 0. Otherwise, it returns infinity.
*/
template<typename MatType>
typename MatType::elem_type Evaluate(const MatType& coordinates) const;
/**
* Apply a proximal step to the given `coordinates`, assuming that the forward
* step took a step of size `stepSize`. This projects `coordinates` back onto
* the surface of the L1-ball with radius `lambda`, if the L1 norm of
* `coordinates` is greater than `lambda`.
*
* This may apply the proximal step multiple times to account for numerical
* stability issues during projection.
*/
template<typename MatType>
void ProximalStep(MatType& coordinates, const double stepSize) const;
//! Get the L1 constraint to use when applying the proximal step.
double Lambda() const { return lambda; }
//! Modify the L1 constraint to use when applying the proximal step.
double& Lambda() { return lambda; }
private:
//! The L1 constraint value to use.
double lambda;
//! Helper function: extract only nonzero elements from sparse objects, or
//! extract the entire dense object.
template<typename MatType>
inline arma::Col<typename MatType::elem_type> ExtractNonzeros(
const MatType& coordinates) const;
template<typename eT>
inline arma::Col<eT> ExtractNonzeros(const arma::SpMat<eT>& coordinates)
const;
};
} // namespace ens
// Include implementation.
#include "l1_constraint_impl.hpp"
#endif
@@ -0,0 +1,201 @@
/**
* @file l1_constraint_impl.hpp
* @author Ryan Curtin
*
* An implementation of the proximal operator for the L1 constraint.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef ENSMALLEN_FBS_L1_CONSTRAINT_IMPL_HPP
#define ENSMALLEN_FBS_L1_CONSTRAINT_IMPL_HPP
// In case it hasn't been included yet.
#include "l1_constraint.hpp"
namespace ens {
inline L1Constraint::L1Constraint(const double lambda) : lambda(lambda)
{
// Nothing to do.
}
template<typename MatType>
typename MatType::elem_type L1Constraint::Evaluate(const MatType& coordinates)
const
{
typedef typename MatType::elem_type eT;
// Allow some amount of tolerance for floating-point errors.
const eT l1Norm = norm(vectorise(coordinates), 1);
if (l1Norm <= lambda)
return eT(0);
else if (std::numeric_limits<eT>::has_infinity)
return std::numeric_limits<eT>::infinity();
else
return std::numeric_limits<eT>::max();
}
template<typename MatType>
void L1Constraint::ProximalStep(MatType& coordinates,
const double /* stepSize */)
const
{
// First determine whether projection is necessary.
if (norm(vectorise(coordinates), 1) <= lambda)
{
return;
}
// An empty vector can't be projected.
if (coordinates.n_elem == 0)
{
return;
}
// We use the algorithm denoted in Figure 2 of the following paper:
//
// ```
// @inproceedings{duchi2008efficient,
// title={Efficient projections onto the L1-ball for learning in high
// dimensions},
// author={Duchi, John and Shalev-Shwartz, Shai and Singer, Yoram and
// Chandra, Tushar},
// booktitle={Proceedings of the 25th international conference on
// Machine learning},
// pages={272--279},
// year={2008}
// }
// ```
//
// This is an iterative algorithm that has a quicksort feel, where we try to
// determine the "pivot" element that tells us how much we need to shrink. In
// the original paper, they maintain lists indicating whether a point is above
// or below the pivot, but it is more expedient (and efficient) to simply copy
// the coordinates array and partially sort it in-place.
typedef typename MatType::elem_type eT;
arma::Col<eT> work = ExtractNonzeros(coordinates);
size_t firstUpperElement = 0;
size_t lastUpperElement = work.n_elem;
eT rho = eT(0); // This is the quantity we aim to find to perform the projection.
eT s = eT(0);
while (lastUpperElement > firstUpperElement)
{
const size_t k = arma::randi<size_t>(
arma::distr_param((int) firstUpperElement, (int) lastUpperElement - 1));
const eT v = work[k];
// Now perform a half-quicksort such that all elements greater than v are in
// the first part of the array.
size_t left = firstUpperElement;
size_t right = lastUpperElement - 1;
while (left <= right)
{
while ((left < lastUpperElement) && (work[left] >= v))
++left;
while ((right > firstUpperElement) && (work[right] < v))
--right;
if (left >= right)
break;
// work[left] is less than v, and work[right] is not. Since we want all
// elements greater than or equal to v on the left, swap.
const eT tmp = work[left];
work[left] = work[right];
work[right] = tmp;
}
// Now, work[0] through work[left - 1] are in the greater set G.
const eT sDelta = accu(work.subvec(firstUpperElement, left - 1));
const size_t rhoDelta = (left - firstUpperElement);
if ((s + sDelta) - ((eT) (rho + rhoDelta)) * v < eT(lambda))
{
s += sDelta;
rho += rhoDelta;
firstUpperElement = left;
}
else
{
// v was an element that was less than rho, so, shrink the array and try
// again with larger elements. We actually want to shrink the array so
// that it does not include v, so we need to find the first element that
// is v (since there may be duplicates).
size_t firstVIndex = left - 1;
while ((work[firstVIndex] == v) && (firstVIndex >= firstUpperElement))
--firstVIndex;
lastUpperElement = firstVIndex + 1;
}
}
const eT theta = (s - eT(lambda)) / rho;
// This is a single-line implementation of the .transform() below; we use the
// single-line implementation so it works with Bandicoot.
//
// coordinates.transform(
// [theta](eT val)
// {
// if (val > 0)
// return std::max(val - theta, eT(0));
// else
// return std::min(val + theta, eT(0));
// });
coordinates = sign(coordinates) % clamp(
abs(coordinates) - theta, eT(0), std::numeric_limits<eT>::max());
// Sanity check: ensure we actually ended up inside the L1 ball. This might
// not happen due to floating-point inaccuracies. If so, try again.
const eT newNorm = norm(coordinates, 1);
if (newNorm > eT(lambda) && eT(lambda) > eT(0))
{
// Shrink the L1 ball by the amount of the error.
eT newLambda = (eT(lambda) - 2 * (newNorm - eT(lambda)));
if (newLambda == eT(lambda))
{
// Make sure we at least remove a few ULPs.
newLambda = eT(lambda) -
5 * (eT(lambda) - eT(std::nexttoward(lambda, 0.0)));
}
L1Constraint newConstraint(newLambda);
newConstraint.ProximalStep(coordinates, 0.0 /* ignored */);
}
}
// Helper function: extract only nonzero elements from sparse objects, or
// extract the entire dense object.
template<typename MatType>
inline arma::Col<typename MatType::elem_type> L1Constraint::ExtractNonzeros(
const MatType& coordinates) const
{
typedef typename MatType::elem_type ElemType;
return conv_to<arma::Col<ElemType>>::from(vectorise(abs(coordinates)));
}
template<typename eT>
inline arma::Col<eT> L1Constraint::ExtractNonzeros(
const arma::SpMat<eT>& coordinates) const
{
arma::Col<eT> result(coordinates.n_nonzero);
typename arma::SpMat<eT>::const_iterator it = coordinates.begin();
size_t i = 0;
while (it != coordinates.end())
{
// Extract only nonzero values. Note we use the absolute value because that
// is what the algorithm requires (not the original value).
result[i] = std::abs(*it);
++it;
++i;
}
return result;
}
} // namespace ens
#endif

Some files were not shown because too many files have changed in this diff Show More