Compare commits

...
753 Commits
Author SHA1 Message Date
Ryan Curtin 8d9c037153 Update and release version 2.14.2. 2020-09-05 10:07:19 -04:00
Ryan Curtin 5df4088cde Merge pull request #223 from coatless/patch-1
Fix implementation of fonesca fleming problem function f1 and f2     type usage and negative signs.
2020-08-29 20:50:56 -04:00
James J Balamuta ec2cd1c2ad Update HISTORY.md 2020-08-27 09:10:07 -05:00
James J Balamuta a18f5fc2db Fix type issue and implementation of fonesca n1 function
- Improves the mathematical description of the problem
- Fixes the implementation of f1 and f2
- Ensure use of `pow()` and `sqrt()` return double
2020-08-27 09:07:44 -05:00
Ryan Curtin 9490d3c651 Add new block for next release to HISTORY.md. 2020-08-21 09:44:57 -04:00
Ryan Curtin 9d3fc459ce Update and release version 2.14.1. 2020-08-21 09:44:57 -04:00
Marcus Edel 0431e31831 Merge pull request #220 from rcurtin/release-quotation-fix
Fix quotation bug in release script.
2020-08-19 23:23:16 +02:00
Ryan Curtin 34f852258a Update HISTORY.md. 2020-08-18 17:36:15 -04:00
Ryan Curtin 2e52643db0 Fix quotation bug. 2020-08-18 17:32:30 -04:00
Ryan Curtin 0445b10ae8 Merge pull request #216 from say4n/fix-release-script
Fix release script
2020-08-16 19:50:50 -04:00
Marcus Edel b7eb6d08e6 Merge branch 'master' into fix-release-script 2020-08-16 19:46:16 +02:00
Marcus Edel 88879f3200 Merge pull request #217 from rcurtin/auglag-float-fix
Set AugLagrangian tolerance based on ElemType.
2020-08-14 15:56:01 +02:00
Ryan Curtin b9987d8a99 Update HISTORY. 2020-08-11 21:37:27 -04:00
Ryan Curtin 3b411c56a6 Adjust tolerance of AugLagrangian according to ElemType. 2020-08-11 21:36:23 -04:00
Sayan GoswamiandRyan Curtin 216c003002 Fix awk syntax to actually use variable substitution (oops).
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-08-12 03:17:22 +05:30
Sayan Goswami eae8fa7602 Trim leading whitespaces (needed for MacOS). 2020-08-11 22:47:43 +05:30
Sayan Goswami 203de9f670 Add changes to HISTORY.md 2020-08-11 22:31:32 +05:30
Sayan Goswami 71c3499cf3 Fix release script. 2020-08-11 22:28:14 +05:30
Marcus Edel a2ddf51396 Merge pull request #214 from say4n/release-2.14.0
Release version 2.14.0: "No Direction Home".
2020-08-11 18:52:00 +02:00
Sayan Goswami 6da0267bc6 Add new block for next release to HISTORY.md. 2020-08-10 23:05:47 +05:30
Sayan Goswami c5673b9ca8 Update and release version 2.14.0. 2020-08-10 23:05:46 +05:30
Ryan Curtin c73c92a2f5 Merge pull request #149 from say4n/nsga2
Add NSGA2
2020-08-10 10:10:47 -04:00
Sayan Goswami 6c9f1ac6e5 Include assert header. 2020-07-30 15:22:30 +05:30
Sayan Goswami 71e609bf2d Use const double instead of const double & for lowerBound and upperBound. 2020-07-30 15:20:36 +05:30
Sayan Goswami ddfb6c36c5 Add Fonseca Fleming function file, oops! 2020-07-30 15:11:23 +05:30
Sayan Goswami c4110d0906 Fix spelling for Fonseca Fleming. 2020-07-30 15:06:58 +05:30
Sayan Goswami ea5a54b77c Add overload for lowerBound, upperBound, add tests for the same. Fix CrowdingDistanceAssignment. 2020-07-30 14:57:11 +05:30
Sayan Goswami e68fbfeabe Add explicit note regarding lowerBound and upperBound in documentation. 2020-07-30 13:52:50 +05:30
Sayan Goswami 5d13117a1f Update documentation on epsilon. 2020-07-30 13:47:40 +05:30
Sayan Goswami d2528f3f11 Set lowerBound to 0 and upperBound as 1 by default.
Note: upper and lower bounds need to be set per variable for the optimization problem being solved.
2020-07-30 13:47:11 +05:30
Sayan Goswami 8579650cb9 Fix indentation to not exceed column limit. 2020-07-30 13:34:05 +05:30
Sayan Goswami 7c1a556209 Remove extra newline. 2020-07-30 13:30:31 +05:30
Sayan Goswami 0c5828ac3e Remove extra newline. 2020-07-30 13:29:31 +05:30
Sayan Goswami c566b4a8c9 Remove extra space. 2020-07-30 13:28:48 +05:30
Sayan Goswami 08dff6ced6 Update NSGA2.Optimize() docstring to reflect changed return ttype. 2020-07-30 13:27:27 +05:30
Sayan Goswami aec42c5fa6 Fix spelling. 2020-07-30 13:24:38 +05:30
Sayan Goswami 457b1cbdd4 Fix docs. 2020-07-30 13:22:54 +05:30
Sayan Goswami 42ee14f08e Fix upper, lower bounds for Fonseca Flemming.
I had forgotten to add the bounds for the third variable.
2020-07-29 17:06:33 +05:30
Sayan Goswami 14e13fc00f Add numVariables to fix Mutate(). 2020-07-29 17:05:11 +05:30
Sayan Goswami f45b6533a6 Use array initialization for bounds instead of string. 2020-07-29 14:05:55 +05:30
Sayan Goswami e8c586c305 Initialize calculatedObjectives with zeros. 2020-07-29 14:05:10 +05:30
Sayan Goswami 021bff545c Use explicit types in callback test, fix typo. 2020-07-29 02:33:17 +05:30
Sayan Goswami 6631165b11 Use ElemType in place of double. 2020-07-28 19:59:47 +05:30
Sayan Goswami 2840027df8 Update MOO description. 2020-07-27 14:55:52 +05:30
Sayan Goswami 9c29738afb Add note about Front() method for MOO. 2020-07-27 14:54:56 +05:30
Sayan Goswami e250f55558 Add generic MO function as example. 2020-07-27 14:52:13 +05:30
Sayan Goswami 20f67a67a5 Merge branch 'master' into nsga2 2020-07-27 14:39:20 +05:30
Sayan GoswamiandRyan Curtin 178e2e0b44 Update doc/optimizers.md
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-27 14:38:14 +05:30
Sayan GoswamiandRyan Curtin 0a3d23aba2 Fix indentation.
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-27 14:37:36 +05:30
Sayan GoswamiandRyan Curtin 9ae8fe3096 Update documentation for Front().
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-27 14:37:16 +05:30
Marcus Edel 8c98f8368d Merge pull request #201 from rcurtin/constrained-optimizer-fixes
Fix constrained optimizer documentation.
2020-07-26 21:45:48 +02:00
Marcus Edel 7aa725b7cf Merge branch 'master' into constrained-optimizer-fixes 2020-07-25 21:03:05 +02:00
Sayan Goswami 69ddc39488 Use min of sum of objectives across entire population as performance measure. 2020-07-24 12:46:00 +05:30
Sayan Goswami 206145bd59 Remove unused variable. 2020-07-23 20:21:41 +05:30
Sayan Goswami 03c4dcba35 Merge branch 'master' into nsga2 2020-07-23 19:54:32 +05:30
Sayan Goswami a8de350b9f Return a single MatType::elem_type performance measure from Optimize(). 2020-07-23 19:53:32 +05:30
Ryan Curtin 852de21b70 Merge pull request #207 from rcurtin/release-script-fixes
Website update release script fixes
2020-07-19 17:01:22 -04:00
Sayan Goswami 1da4b449cd Merge branch 'master' into nsga2 2020-07-20 01:33:28 +05:30
Ryan Curtin 40a6046159 Add note to rebase to generated PR description. 2020-07-18 14:09:29 -04:00
Ryan Curtin e6d8f85a5b Update HISTORY. 2020-07-18 14:05:25 -04:00
Ryan Curtin 2d7b59be7b Update documentation and error output. 2020-07-18 14:03:49 -04:00
Ryan Curtin 7ac88b6385 Fixes to website release script. 2020-07-18 14:02:49 -04:00
Ryan Curtin 38229276c5 Merge pull request #206 from rcurtin/release-2.13.0
Release version 2.13.0: "Automatically Automated Automation"
2020-07-18 13:29:54 -04:00
Ryan Curtin d5c67b10e7 Add new block for next release to HISTORY.md. 2020-07-15 14:51:32 -04:00
Ryan Curtin 5efb1eb637 Update and release version 2.13.0. 2020-07-15 14:51:32 -04:00
Ryan Curtin b42aec9753 Fix changelog extraction script bits. 2020-07-15 14:50:46 -04:00
Ryan Curtin a3c38f6c78 Fix syntax in HISTORY.md. 2020-07-15 14:50:32 -04:00
Ryan Curtin dd9215a1ab Further updates to release script. 2020-07-15 14:45:13 -04:00
Ryan Curtin ad29230f43 Update release script. 2020-07-15 14:41:58 -04:00
Sayan Goswami 3c6aa81ca4 Return objective values from Optimize().
Using separate function Front() to get the best front.
2020-07-15 13:23:53 +05:30
Sayan Goswami 1c79296626 Add spacing as per style guide. 2020-07-15 10:43:36 +05:30
Sayan GoswamiandMarcus Edel db4a84b939 Adjust spacing to follow code style.
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-07-15 02:19:27 +05:30
Sayan GoswamiandMarcus Edel bfff7963a5 Adjust spacing to follow code style.
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-07-15 02:19:11 +05:30
Sayan GoswamiandRyan Curtin a688988997 Update doc/optimizers.md
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-14 22:09:43 +05:30
Sayan GoswamiandRyan Curtin 88f6500201 Update doc/function_types.md
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-14 22:09:05 +05:30
Sayan GoswamiandRyan Curtin fab76f6002 Update doc/function_types.md
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-14 22:08:53 +05:30
Sayan GoswamiandRyan Curtin c0d3379bc3 Update HISTORY.md
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-14 22:08:38 +05:30
Sayan Goswami 75ae7efbc4 Merge branch 'master' into nsga2 2020-07-14 20:23:46 +05:30
Sayan Goswami b64a5a3627 Fix Fonseca Flemming implementation. 2020-07-14 20:20:46 +05:30
Ryan Curtin a63ea6d2f0 Merge pull request #165 from shrit/early_stopping
Early stopping at loss min on validation set
2020-07-13 20:59:32 -04:00
Omar Shrit f7aa5e6a85 Add a using in order to avoid a major version bump
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-07 12:04:59 +02:00
shritandRyan Curtin 130057ecb9 Update to show example use case
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-07 10:28:18 +02:00
shritandRyan Curtin 8fff9e8d75 Update class name according to typedef
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-07 10:25:15 +02:00
shritandRyan Curtin 8fb5903191 Use arma::mat instead of template type
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-07 10:24:39 +02:00
Omar Shrit ea1c4e494a Fix typos error
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:08:43 +02:00
Omar Shrit 742237f150 Update documentation with the new constructor of early_stop_at_min_loss.
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:08:43 +02:00
Omar Shrit 8f45d8708e Replace climits with limits, and remove limits from callbacks_test
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:08:43 +02:00
Omar Shrit a799406f99 Update HISTORY with the early stop callback new constructor
Now we can evaluate any function loss directly using a lambda
function inside the constructor of early stop at min loss

Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:07:32 +02:00
shritandMarcus Edel 14272ea714 Fix style
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-07-04 23:06:10 +02:00
shritandMarcus Edel 64d94c20ab Remove non used header
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-07-04 23:06:10 +02:00
shritandMarcus Edel d8edac89d8 Update include/ensmallen_bits/callbacks/early_stop_at_min_loss.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-07-04 23:06:10 +02:00
shritandMarcus Edel 88c7fa4236 Update include/ensmallen_bits/callbacks/early_stop_at_min_loss.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-07-04 23:06:10 +02:00
Omar Shrit 205bdbbb18 Remove non used parameters
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:10 +02:00
Omar Shrit 3c13753540 Examine the minValue when we are close to 10 or very far
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:10 +02:00
Omar Shrit c1fd65f93f Add custom lambda function test
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:10 +02:00
shritandRyan Curtin d3efd15745 Update include/ensmallen_bits/callbacks/early_stop_at_min_loss.hpp
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-04 23:06:09 +02:00
Omar Shrit 0dbc5006a4 Resolve name conflicts
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit 2d18880679 Remove logisitic regression validation from tests
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit f36769aa5e Remove logistic_regression_function_validation wrapper
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit c14a317364 Improve style
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit 8885c9f8de Remove output stream for this callbacks
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
shritandRyan Curtin ca5a32947a Update include/ensmallen_bits/callbacks/early_stop_at_min_loss.hpp
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-04 23:06:09 +02:00
shritandRyan Curtin bfd9cd6861 Update include/ensmallen_bits/function/add_evaluate.hpp
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-04 23:06:09 +02:00
shritandRyan Curtin 1de185660d Update include/ensmallen_bits/callbacks/early_stop_at_min_loss.hpp
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-04 23:06:09 +02:00
shritandRyan Curtin ef75756287 Update include/ensmallen_bits/callbacks/early_stop_at_min_loss.hpp
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-04 23:06:09 +02:00
shritandRyan Curtin 017a15796b Update include/ensmallen_bits/callbacks/early_stop_at_min_loss.hpp
Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-07-04 23:06:09 +02:00
Omar Shrit 849cd34e16 Update test with the new callbacks
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit 566cccdc2b Adding boolean to check the std::function
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit f8e8106335 Remove AnnType, keep only std::function instead
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit c4e785e555 Adding a Early stop costr with callback
Signed-off-by: Omar Shrit <omar@shrit.me>
2020-07-04 23:06:09 +02:00
Omar Shrit a022be0202 Adding callback test function for early stop
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 32b8285a78 Adding LogisiticRegression on Validation proposed by Marcus Edel
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 8f20f90c53 Adding two templates for features and label matrix type
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 7821bdd294 Remove added header for a old EarlyStopOnValidation
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 34b5deba3a Adding reference to the network model to access to evaluate function
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 44dbd8f3a5 Merge this class with the original one
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 6c53e56667 Remove the interface for Evaluate(mat, mat)
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit dee5a7f974 Re-add evaluate(arma, arma) if it has evaluate
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit b42d2e1220 Adding const type qualifier
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 48ec6b8dd2 Remove std::move, fix alignment
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit addf2f3f4e Adding evalute function
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit eccbb5a8c0 Merge two early_stop classes in one class
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit a16f309bc2 Remove extra lines, Fix filename and other comments
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 38573580b0 Add Early Stop validation callback in ensmallen global header
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 9f1ed3dd44 Print validation loss to monitor it during training
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 7fb25b48de Overload necessary evaluate function required for callbacks
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Omar Shrit 5fd19ed06c Adding first implementation on early_stop_at_min_loss_on_validation
This callback measure the min loss on the validation set rather
than the train set.

Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-07-04 23:06:09 +02:00
Ryan Curtin 64f1dc4c5b Update HISTORY.md. 2020-06-28 11:57:16 -04:00
Ryan Curtin f4feccb442 Add initialization of uninitialized parameter. 2020-06-28 11:56:15 -04:00
Ryan Curtin 31457c291c Oops, one more debug output. 2020-06-22 20:19:21 -04:00
Ryan Curtin 397e6e63dc Oops, remove debugging information. 2020-06-22 20:18:14 -04:00
Ryan Curtin 7d84591eab Allow termination on the first iteration if the gradient is really 0. 2020-06-22 20:13:59 -04:00
Ryan Curtin 0c31e61147 Fix missing comma. 2020-06-22 20:13:26 -04:00
Ryan Curtin 680ab16006 Clarify documentation on constrained function optimization. 2020-06-22 20:13:16 -04:00
Sayan Goswami d49775b28f Fix variable name (oops)
Use camel case.
2020-06-15 13:28:49 +05:30
Sayan Goswami c26c0cf249 Merge branch 'master' into nsga2 2020-06-15 13:20:18 +05:30
Sayan GoswamiandMarcus Edel fdb6cdc72f Update tests/nsga2_test.cpp
Use camel case names.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:45 +05:30
Sayan GoswamiandMarcus Edel c1cb6d1ff1 Update include/ensmallen_bits/nsga2/nsga2_impl.hpp
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:41 +05:30
Sayan GoswamiandMarcus Edel f0cbba7f98 Update include/ensmallen_bits/nsga2/nsga2_impl.hpp
Use uniform distribution for initialisation.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:35 +05:30
Sayan GoswamiandMarcus Edel ca37423560 Update tests/nsga2_test.cpp
Use camel case names.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:32 +05:30
Sayan GoswamiandMarcus Edel f68144d818 Update tests/nsga2_test.cpp
Use camel case names.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:25 +05:30
Sayan GoswamiandMarcus Edel 776f5aa546 Update tests/nsga2_test.cpp
Remove range based for loops.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:22 +05:30
Sayan GoswamiandMarcus Edel 60ccb22f45 Update tests/nsga2_test.cpp
Use camel case names.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:14 +05:30
Sayan GoswamiandMarcus Edel e014370ec8 Update tests/nsga2_test.cpp
Use camel case names.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:18:07 +05:30
Sayan GoswamiandMarcus Edel 076e9648f4 Use 1.0 instead of 1.0f for floats.
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:17:55 +05:30
Sayan GoswamiandMarcus Edel 778b7ef8a0 Update tests/nsga2_test.cpp
Change optimiser parameter as per suggestion.

Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:17:47 +05:30
Sayan GoswamiandMarcus Edel 525724c0d1 Update tolerance (use 10^-6 instead of 10^-5).
Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
2020-06-15 13:17:22 +05:30
Dominique Lasserre 12fe7e0403 Fix cmake package export.
* Fix spelling mistake introduced in 369046f

* Don't export compiler warning flags to interface.
2020-06-15 13:17:04 +05:30
Ryan Birmingham 70a2be8646 Merge pull request #198 from Lasall/cmake-export-fix
Fix cmake package export.
2020-05-06 10:50:10 -04:00
Dominique Lasserre 1aa9e2e991 Fix cmake package export.
* Fix spelling mistake introduced in 369046f

* Don't export compiler warning flags to interface.
2020-05-06 12:25:28 +02:00
Marcus Edel 132e9679f7 Merge branch 'master' into nsga2 2020-04-27 23:21:07 +02:00
Ryan Curtin 82817a6e3b Revert "Remove new HISTORY block for release."
This reverts commit c6a04c02c2.
2020-04-21 13:48:28 -04:00
Ryan Curtin c6a04c02c2 Remove new HISTORY block for release. 2020-04-21 13:48:07 -04:00
Ryan Curtin fecd7d652d Merge pull request #196 from rcurtin/release-2.12.1
Release version 2.12.1
2020-04-21 13:41:47 -04:00
Ryan Birmingham 86e5f679bc Remove extra lines 2020-04-20 14:53:46 -04:00
Ryan Birmingham 371177b80c Merge branch 'master' into release-2.12.1 2020-04-20 14:35:30 -04:00
Sayan Goswami c3a5a56fc8 Resolve merge conflict with upstream/master 2020-04-19 18:06:28 +05:30
Conrad Sanderson de19dddc5d minor documentation fixes (#197)
* "is an an" -> "is an"

* grammar fix

* grammar fix

* update HISTORY.md
2020-04-17 10:35:37 -04:00
Ryan Curtin 7064fbeb60 Add new block for next release to HISTORY.md. 2020-04-16 20:40:32 -04:00
Ryan Curtin 09fccad4a4 Update and release version 2.12.1. 2020-04-16 20:40:32 -04:00
Ryan Curtin c0e08158b2 Allow SSH remotes. 2020-04-16 20:39:35 -04:00
Ryan Curtin 2d25bd933e Update HISTORY for release. 2020-04-16 20:37:08 -04:00
Marcus Edel c19da5560d Merge pull request #195 from say4n/patch-1
Update HISTORY.md.
2020-04-16 20:39:59 +02:00
Gaurav SinghandRyan Curtin a005414da0 Fixing ProgressBar() (#181)
* Initial commit.

* Minor fixes.

* Style changes.

* Minor style changes.

* Minor Changes

* Changes in HISTORY.md and adding a test.

* minor changes.

* Update HISTORY.md

Co-Authored-By: Ryan Curtin <ryan@ratml.org>

* Update include/ensmallen_bits/callbacks/progress_bar.hpp

Co-Authored-By: Ryan Curtin <ryan@ratml.org>

* Incorporating the changes.

* Solving style issues

* Solving merge conflicts.

* Adding tests and correcting the epoch conditionals.

* Minor changes in tests.

* Minor style changes

* Minor changes.

* Style changes.

* Changes in callback tests.

* Style Changes.

* Update include/ensmallen_bits/callbacks/progress_bar.hpp

Co-Authored-By: Ryan Curtin <ryan@ratml.org>

* Update tests/callbacks_test.cpp

Co-Authored-By: Ryan Curtin <ryan@ratml.org>

* Update tests/callbacks_test.cpp

Co-Authored-By: Ryan Curtin <ryan@ratml.org>

* Changes in tests.

Co-authored-by: Ryan Curtin <ryan@ratml.org>
2020-04-16 20:06:46 +02:00
Ryan Curtin a8c29763c0 Merge pull request #194 from conradsnicta/traits-fix
handle SpSubview_col and SpSubview_row in Armadillo 9.870
2020-04-15 22:30:03 -04:00
Ryan CurtinandMarcus Edel 324016696c Update HISTORY.md
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
2020-04-15 22:29:53 -04:00
Sayan Goswami 5b5da69148 Update HISTORY.md
Add missing end quote. :)
2020-04-15 23:08:36 +05:30
conrad c05be60ca5 update HISTORY.md 2020-04-15 13:37:56 +10:00
conrad 514cfcd8c0 update HISTORY.md 2020-04-15 13:36:16 +10:00
conrad 9d30e3391b add trais for arma::SpSubview_col and arma::SpSubview_col 2020-04-15 13:25:35 +10:00
Sayan Goswami f6ce7fe753 Add documentation for multi objective fucntion type. 2020-04-13 20:25:29 +05:30
Sayan Goswami b0d7ea5e7d Remove newline, fix indentation 2020-04-13 20:07:55 +05:30
Ryan Curtin 2615335321 Merge pull request #192 from rcurtin/update-release-scripts
Update release scripts
2020-04-03 10:12:14 -04:00
Sayan Goswami bde5152db8 Make objectives public members, use explicit type instead of auto keyword 2020-04-03 13:17:14 +05:30
Sayan Goswami e06d407afc Use explicit types instead of auto 2020-04-03 13:16:11 +05:30
Sayan Goswami 75183d00f7 Fix typo, add braces, correct indentation, spacing 2020-04-03 12:55:37 +05:30
Sayan Goswami 76b71b2594 Remove unnecessary line break 2020-04-03 12:48:31 +05:30
Sayan Goswami c187ad5b0c Wrap lines to <80 columns 2020-04-03 12:47:46 +05:30
Sayan Goswami 0dc99f0831 Reposition multi-objective optimization section to before constrained functions 2020-04-03 12:46:06 +05:30
Sayan Goswami 13c1857c57 Add period to end of line 2020-04-03 12:45:10 +05:30
Sayan Goswami 13bdfc1f5c Merge branch 'nsga2' from remote 2020-04-03 12:40:47 +05:30
Sayan Goswami 2ac289a20d Fix typos 2020-04-03 12:39:13 +05:30
Ryan Curtin f1764fa62f Remove now-unnecessary output. 2020-03-29 18:22:29 -04:00
Ryan Curtin b8cb8e660f Clean up and refactor release scripts so admin access is not needed. 2020-03-29 18:21:53 -04:00
Sayan Goswami e9867d927c Merge branch 'master' into nsga2 2020-03-29 03:54:24 -04:00
Ryan Curtin 82368c4c05 Add new HISTORY block. 2020-03-28 13:06:38 -04:00
Ryan Curtin 12a2522df7 Update and release version 2.12.0. 2020-03-28 13:05:32 -04:00
Ryan Curtin a1c65c55d5 Update HISTORY for release. 2020-03-28 13:05:24 -04:00
Ryan Curtin 761b2dbd18 Minor style fix. 2020-03-28 12:52:00 -04:00
favre49 c76d18b704 Merge pull request #185 from jwdinius/remove-sdp-depr
remove sdp deprecated methods
2020-03-28 21:41:41 +05:30
Sayan Goswami 8fb4578758 Merge remote-tracking branch 'upstream/master' into nsga2 2020-03-27 10:18:58 +05:30
favre49 f948ea6119 Merge pull request #186 from rcurtin/readme-new-logo
Use new ensmallen logo
2020-03-27 09:26:39 +05:30
Ryan Curtin be08b5639d Merge branch 'master' into readme-new-logo 2020-03-26 22:46:07 -04:00
Ryan Curtin 2d5f6d6d37 Change to 'numerical optimization'. 2020-03-26 22:43:13 -04:00
Ryan Curtin d37205b59a Try adding a new logo. 2020-03-26 22:40:11 -04:00
Joe Dinius f29fe7650b address issue #184 - remove sdp deprecated methods 2020-03-26 07:32:09 -07:00
favre49 293cc83cbf Merge pull request #183 from gaurav-singh1998/minor-cmaes-sigma-fix
Correction in the computation of sigma for the next population in CMA-ES.
2020-03-24 17:07:11 +05:30
Gaurav Singh fa2ada3823 Merge branch 'master' into minor-cmaes-sigma-fix 2020-03-24 00:51:37 +05:30
Gaurav Singh f3ed6bb0b1 Changes in HISTORY.md 2020-03-24 00:47:14 +05:30
Gaurav Singh d792bfbc24 Minor Changes. 2020-03-23 20:35:04 +05:30
favre49 7bad0ed0e0 Update include/ensmallen_bits/cmaes/cmaes_impl.hpp 2020-03-21 19:25:17 +05:30
Gaurav Singhandfavre49 a984138685 Update include/ensmallen_bits/cmaes/cmaes_impl.hpp
Co-Authored-By: favre49 <40389657+favre49@users.noreply.github.com>
2020-03-21 19:20:44 +05:30
Gaurav Singh e2b8031fb8 Initial commit. 2020-03-21 17:49:47 +05:30
Sayan Goswami 7d8ac2c05a Remove ambiguity with example in documentation 2020-03-19 17:47:53 +05:30
Sayan Goswami cee6461e69 Fix indentation, spacing, rephrase documentation 2020-03-19 17:41:41 +05:30
Sayan Goswami b3052b9c92 Repharse doc, fix indentation, change bounds to use reference, use camel case 2020-03-15 19:17:12 +05:30
Marcus Edel 7d8707d9f3 Merge branch 'master' into nsga2 2020-03-14 00:28:32 +01:00
Ryan Curtin 3cb3827671 Add new HISTORY block. 2020-03-11 21:06:35 -04:00
Ryan Curtin a174206f99 Update and release version 2.11.5. 2020-03-11 21:05:39 -04:00
Ryan Curtin ef02b18a6f Update HISTORY for release. 2020-03-11 21:05:22 -04:00
Marcus Edel dbd23f3a4a Merge pull request #177 from rcurtin/mathematical-to-numerical
Change 'mathematical optimization' to 'numerical optimization'.
2020-03-09 23:23:00 +01:00
Ryan Curtin d08aa13dab Update history. 2020-03-09 07:44:05 -04:00
Ryan Curtin 60f0d58809 Change 'mathematical' to 'numerical'.
This better matches the existing literature and is likely to be less confusing
to people who come across the term.
2020-03-09 07:41:42 -04:00
Sayan Goswami 594d79369a Fix compiler warnings, change test params 2020-03-08 01:24:39 +05:30
Sayan Goswami e5353694f4 Fix punctuation, indentation, line breaks 2020-03-07 16:41:09 +05:30
Sayan Goswami d4ce416b39 Adjust hyperparameters to fix failing tests 2020-03-07 16:08:09 +05:30
Sayan Goswami b01eb26829 Adjust hyperparameters to fix failing tests 2020-03-07 15:59:12 +05:30
Sayan Goswami 16165f8ceb Fix typo 2020-03-07 09:42:15 +05:30
Sayan Goswami 2c6418b445 Merge branch 'master' into nsga2 2020-03-04 14:01:48 -05:00
Sayan Goswami 8c9f992e3d Update history 2020-03-05 00:29:20 +05:30
Sayan Goswami bb37702917 Fix typo, indentation, line breaks 2020-03-04 21:22:40 +05:30
Ryan Curtin b35b5acbdc Update and release version 2.11.4. 2020-03-03 12:34:33 -05:00
Ryan Curtin 8d187ae4e5 Update HISTORY to latest stable version. 2020-03-03 12:34:04 -05:00
Ryan Curtin 3ee4bcad40 Update HISTORY for #174 merge. 2020-03-03 11:48:30 -05:00
Ryan Curtin 4fdfc97539 Merge pull request #174 from zoq/doc-fix
Example doc fix + add/use default argument.
2020-03-03 11:47:46 -05:00
Ryan Birmingham 777bca8d1d Re-fix Ensmallen History Mod Checker (#175)
* test without history mod

* try ysing name only and wc

* give some info in test itself

* script fix?

* ensure adding history fixes build
2020-03-03 00:37:54 +01:00
Marcus Edel cd56df2061 Add/use default type. 2020-03-01 17:51:55 +01:00
Marcus Edel 1fd6eebe47 The L_BFGS optimizer doesn't use a template. 2020-03-01 17:51:08 +01:00
Ryan Birmingham 29ff827019 not-so-quick fix sanity check for outside of PRs (#172)
Travis CI Staged Build Config
2020-02-29 20:26:54 -05:00
Marcus Edel 08994c52ee Merge pull request #171 from coatless/require-history-entry
Require history entry.
2020-02-27 19:25:40 +01:00
James Balamuta 321f7201d6 EOL issue 2020-02-26 14:15:54 -06:00
James Balamuta 882ecf3122 Revise... 2020-02-26 14:05:33 -06:00
James J Balamuta e28c0c0e4d Exit to avoid duplicate test run 2020-02-26 11:56:51 -06:00
Sayan Goswami 24fa27655d Add documentation for multi objective functions 2020-02-26 02:28:11 +05:30
Sayan Goswami 9618226d84 Fix typo, move includes to ensmallen.hpp, add newline! 2020-02-26 01:49:07 +05:30
James Balamuta 09291cecf4 Enable sanity check with jenkins for history entry (h/t @birm) 2020-02-24 16:02:04 -06:00
James Balamuta 2e9e1e7dde Bump to new version in HISTORY.md 2020-02-19 20:56:44 -06:00
James Balamuta 4cd83b417b Wording tweaks. 2020-02-19 20:55:25 -06:00
James Balamuta 8c98650fb6 Update with latest release commit... 2020-02-19 20:53:25 -06:00
James Balamuta 86836a2a6b Tweak header 2020-02-19 16:38:45 -06:00
James Balamuta d6442a0a0f Fix check 2020-02-19 16:19:52 -06:00
James Balamuta d6051fb393 Add a note to history 2020-02-19 16:15:35 -06:00
James Balamuta f84655b959 Add history check PR update 2020-02-19 16:09:32 -06:00
Ryan Curtin a5891f11a2 Update and release version 2.11.3. 2020-02-19 17:08:04 -05:00
Ryan Curtin 8663a12fac Update HISTORY. 2020-02-19 17:07:46 -05:00
James Balamuta 7f13c8beac Rename from rel to scripts 2020-02-19 16:06:10 -06:00
Marcus Edel f7653343b7 Merge pull request #169 from mlpack/conradsnicta-readme-cmake-note
Proposed note on cmake requirements.
2020-02-19 22:53:11 +01:00
Marcus Edel 078733295d Merge pull request #170 from rcurtin/history-fix
Update HISTORY.
2020-02-19 21:18:22 +01:00
Ryan Curtin c6d233c45e Merge remote-tracking branch 'origin/master' into history-fix 2020-02-19 08:57:26 -05:00
Ryan Curtin e6adb5e9e5 Update HISTORY. 2020-02-19 08:55:29 -05:00
Conrad SandersonandRyan Curtin 55e5f72cf5 more details
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2020-02-19 04:54:07 +01:00
Conrad Sanderson 0ac60f2e0f proposed note on cmake requirements 2020-02-19 04:35:03 +01:00
Ryan Curtin 4cf66255cf Merge pull request #167 from rcurtin/memory-fix
Fix memory leak
2020-02-18 21:10:29 -05:00
Ryan Curtin 614ba62a5d Merge branch 'favre49-cmake' 2020-02-18 13:40:32 -05:00
Ryan Curtin 67a94b2236 Add newline at end of file. 2020-02-18 13:39:30 -05:00
Ryan Curtin 013a496203 Merge branch 'cmake' of https://github.com/favre49/ensmallen into favre49-cmake 2020-02-18 13:39:17 -05:00
Ryan Curtin 44181d9990 Fix memory leak. 2020-02-17 16:50:14 -05:00
Ryan Curtin 854f0f34ad Merge pull request #158 from rcurtin/appveyor-config
AppVeyor config
2020-02-17 16:26:42 -05:00
Ryan Birmingham 1625cc14e1 Merge pull request #166 from shrit/citations
Adding bib tex format in the README
2020-02-17 14:34:04 -05:00
Omar Shrit fa07a27498 Keep the original version in addition to bibtex
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-02-17 19:39:32 +01:00
Ryan Curtin 1d5234fbba Remove possibly-unnecessary ARMADILLO_LIBRARY. 2020-02-17 11:55:23 -05:00
Omar Shrit d9ca887eb6 Adding digital object identifier to bibtex
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-02-17 11:56:47 +01:00
Omar Shrit 0a66f6cd2c Adding bib tex format in the README
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-02-15 21:25:34 +01:00
Ryan Curtin c0ea1ed4e7 Remove unnecessary line. 2020-02-13 20:29:34 -05:00
Sayan Goswami dd7fd0a3af Merge branch 'master' into nsga2 2020-02-13 00:14:52 -05:00
Sayan Goswami 603d9a18c1 Add NSGA2 Callback test 2020-02-13 10:41:12 +05:30
Sayan Goswami a11974a4ea Update documentation
Remove 'Multi-objective functions', update NSGA2 Optimize() example
2020-02-10 23:07:35 +05:30
Sayan Goswami 6b7c1dac8c Use unnamed arguments for SFINAE base case
Fixes -Werror=unused-parameter
2020-02-10 11:16:13 +05:30
Sayan Goswami cffac3ce6b Add std::tuple to SCH N.1 2020-02-10 11:04:33 +05:30
Sayan Goswami 47824790e6 Remove redundant typedef 2020-02-10 10:41:51 +05:30
Sayan Goswami be24de787c Fix compiler errors
SFINAE to the rescue!
2020-02-10 10:32:09 +05:30
Ryan Curtin 1828468a0a Merge pull request #164 from shrit/execution_right
Remove execution right from headers
2020-02-09 12:50:07 -07:00
Sayan Goswami 216d1dfdbf Use multiple variadic templates — compile failure! 2020-02-09 23:26:57 +05:30
Omar Shrit b8bf5dae1a Remove execution right from headers
Signed-off-by: Omar Shrit <shrit@lri.fr>
2020-02-08 23:03:44 +01:00
Ryan Curtin 42313d38d0 Adjust tolerances. 2020-01-28 18:02:48 -05:00
Ryan Curtin aa9bed34a7 Try running the test alone. 2020-01-24 14:09:48 -05:00
Ryan Curtin 2ce08eb861 Focus on failing test. 2020-01-24 12:49:14 -05:00
Ryan Curtin 8cb84ca9e6 Copy library in the same way the mlpack config does. 2020-01-24 11:58:17 -05:00
Ryan Curtin a8d45cfbd8 Use powershell to copy? What is happening? 2020-01-24 11:15:58 -05:00
Ryan Curtin 10ee5c68c6 Different copy? 2020-01-24 10:53:40 -05:00
Ryan Curtin 0873421fd7 Ok, try copying libopenblas.dll. 2020-01-23 17:11:36 -05:00
Ryan Curtin db36e89305 Oops, forgot the file extension. 2020-01-23 13:44:44 -05:00
Ryan Curtin 5188145f92 Fix path. 2020-01-23 13:14:24 -05:00
Ryan Curtin d4572a0062 Try again using the correct (hopefully) dumpbin. 2020-01-23 12:31:22 -05:00
Ryan Curtin a5f1ab57dc Try to print all needed DLLs. 2020-01-23 10:32:12 -05:00
Sayan Goswami da52154072 Add doc for Multi-objective functions 2020-01-23 15:58:55 +05:30
Sayan Goswami 5d0d9f9a82 Update docs; add getters, setters; use arma::vec instead of arma::mat 2020-01-23 15:45:03 +05:30
Sayan Goswami 532040f356 Add lower, upper bound as optimizer parameter 2020-01-23 15:29:58 +05:30
Ryan Curtin 197fff8c8f Try copying shared objects to working directory. 2020-01-22 13:27:22 -05:00
Ryan Curtin 7e3c41a48a Provide verbose output from tests. 2020-01-22 11:52:25 -05:00
Ryan Curtin 4ee12b0c47 Specify configuration to run tests. 2020-01-21 21:37:08 -05:00
Ryan Curtin 37a0861980 Other little fixes for compilation.
(Should have tested it all first.)
2020-01-21 20:39:33 -05:00
Ryan Curtin 03a717fcd4 Huh, I must not be very smart. 2020-01-21 19:14:47 -05:00
Ryan Curtin da3373464f Fix std::min types for pedantic MSVC. 2020-01-21 19:10:39 -05:00
Ryan Curtin 1e5995571e These might also be necessary. 2020-01-21 17:21:13 -05:00
Ryan Curtin 2764266a44 Fix path. 2020-01-21 11:49:59 -05:00
Ryan Curtin 65463566d4 Could spacing be an issue here? 2020-01-21 11:40:12 -05:00
Marcus Edel 51614c216e Merge pull request #162 from mlpack/conradsnicta-patch-1
Update history.
2020-01-16 19:18:00 +01:00
Conrad Sanderson c2637c0e20 update history 2020-01-16 03:41:18 +01:00
Conrad Sanderson 5469374a2a prevent spurious compiler warnings (#161) 2020-01-16 03:37:14 +01:00
Ryan Curtin b67c673639 Add new block for history. 2020-01-15 10:23:23 -05:00
Ryan Curtin ba0897d57f Update and release version 2.11.2. 2020-01-15 10:22:29 -05:00
Ryan Curtin c8bcbbf430 Update history. 2020-01-15 10:21:37 -05:00
Ryan Curtin 226352ae9f Merge pull request #159 from zoq/lookahead-maxiterations
Fix Lookahead MaxIterations() check
2020-01-15 10:20:45 -05:00
Marcus Edel fe9c6f3c0e Fix Lookahead MaxIterations() check. 2020-01-12 00:29:24 +01:00
Ryan Curtin 11e9ee7c54 Build Armadillo in C:\projects\armadillo. 2020-01-10 13:42:41 -05:00
Ryan Curtin 64d3f6e5f8 Move up to the right directory. 2020-01-10 13:23:38 -05:00
Ryan Curtin 3eff5848b0 Fix syntax. 2020-01-10 13:16:06 -05:00
Ryan Curtin 4ec4f50324 Fix directories. 2020-01-10 13:07:47 -05:00
Ryan Curtin dced93ed12 Update paths. 2020-01-10 09:52:48 -05:00
Ryan Curtin f5464ae540 Whitespace change to trigger build. 2020-01-10 09:09:11 -05:00
Sayan Goswami 3498b0f01b Remove unnecessary logging 2020-01-10 13:43:58 +05:30
Sayan Goswami b8695df78a Add lower, upper bounds for mutation 2020-01-10 13:12:37 +05:30
Ryan Curtin 2fd30ef5dc Add an attempt at an AppVeyor config. 2020-01-09 18:17:58 -05:00
Marcus Edel 4a35fd6e1b Merge pull request #155 from zoq/callback-pass-fix
Fix Callback passing for SGD based optimizers.
2020-01-09 17:15:24 +01:00
Marcus Edel 54e2d408b2 Merge branch 'master' into callback-pass-fix 2020-01-08 19:38:45 +01:00
Sayan Goswami f74619fe7c Add Fonseca Flemming test function 2020-01-08 18:35:15 +05:30
Sayan Goswami 0b14d94dc9 Fix braces, remove redundant check (if-else instead of if) 2020-01-06 17:12:00 +05:30
Sayan Goswami 86276fa29b Fix typos, line width, remove unnecessary logging, braces, add checks in Optimizer 2020-01-06 15:10:30 +05:30
Sayan Goswami 78662ac930 Add doc, rename MutationSize to MutationStrength 2020-01-04 18:41:44 +05:30
favre49 73c03b5e70 Merge pull request #156 from rcurtin/float-tol-fixes
Fix float types and tolerances in some tests.
2020-01-04 16:43:21 +05:30
favre49 0202d479c4 Merge pull request #157 from say4n/typo-fix-SCHn2
Fix typo
2020-01-04 16:42:00 +05:30
Sayan Goswami 9fe48976bf Add template arg for calculatedObjectives, add const reference in Crossover, fix indentation 2020-01-04 13:49:23 +05:30
Sayan Goswami 419b35a1a7 Re-introduce range based checks 2020-01-04 11:43:28 +05:30
Sayan Goswami 177ce878a8 Fix typos, remove unnecessary fields from citation 2020-01-04 11:42:59 +05:30
Sayan Goswami 9785fdf462 Fix typo 2020-01-03 16:00:23 +05:30
Sayan Goswami e54c7ab7a1 Add documentation 2020-01-03 15:48:26 +05:30
Sayan Goswami 39fe0e96d2 Fix indentation, code style. Use const reference for parameters, remove redundant variables, remove auto keyword, fix erroneous check, change return type of Schaffer N.1 function 2020-01-03 14:04:04 +05:30
Marcus Edel 074e6c57c5 Update history (#155). 2020-01-02 17:59:07 +01:00
Ryan Curtin fe2e20d798 Update history. 2020-01-02 11:22:59 -05:00
Ryan Curtin a3afeefc83 Fix float types and tolerances in some tests. 2020-01-02 11:17:52 -05:00
Rahul Prabhu bed37733ab Removed ARCH_INDEPENDENT 2020-01-02 20:03:56 +05:30
Rahul Prabhu 369046f99d Changed case where necessary 2020-01-02 19:28:47 +05:30
Sayan Goswami eee7b78da4 Fix alignment of braces 2020-01-02 18:38:40 +05:30
Sayan Goswami 1fa339cd57 Remove unused variable 2020-01-02 18:32:05 +05:30
Sayan Goswami 1c734d10ed Merge branch 'master' into nsga2 2020-01-02 18:26:20 +05:30
Sayan Goswami 21df412cb0 Change test to just print 2020-01-02 18:11:12 +05:30
Sayan Goswami 73c6fbd92f Rename MaxGenerations, CrossoverRate, MutationProbability, MutationSize; Correct indentation 2020-01-02 18:10:45 +05:30
Sayan Goswami dbbf217388 Arrange includes alphabetically 2020-01-02 18:05:27 +05:30
Marcus Edel 86e289691a Fix callback passing for SGD based optimizers. 2019-12-31 20:21:50 +01:00
Rahul Prabhu 8ef70e346b Added Conrad's suggestion. Must fix styles. 2019-12-30 13:57:07 +05:30
Roberto Hueso Gomez e9bb969b1f Update HISTORY after release 2019-12-28 17:37:50 +01:00
Roberto Hueso Gomez 421c667eac Update and release version 2.11.1. 2019-12-28 17:26:23 +01:00
Roberto Hueso Gomez fab7c1c82e Update HISTORY for release 2019-12-28 15:40:45 +01:00
favre49 95711477bc Merge pull request #154 from rcurtin/doc-function-types-fix
Add some missing optimizer types.
2019-12-28 18:20:43 +05:30
Ryan Curtin 839b94dec4 Merge pull request #153 from zoq/lookahead-type-fix
Lookahead - Synchronization period type fix
2019-12-27 23:33:44 -05:00
Rahul Prabhu fc5ce2ff46 Added warning flags and exporting scripts. 2019-12-27 21:41:52 +05:30
Marcus Edel a00189035a Update HISTORY.md (Lookahead type fix). 2019-12-27 14:03:41 +01:00
Marcus Edel a8bddce1da Merge branch 'master' into lookahead-type-fix 2019-12-27 14:01:35 +01:00
Ryan Curtin 31bb39a7c9 Add some missing optimizer types. 2019-12-26 22:35:53 -05:00
Marcus Edel 4e8c34e33b Use the correct return type for the synchronization period (k). 2019-12-26 21:41:24 +01:00
Sayan Goswami d7d62b0483 Change test parameters, use random initialization for coordinates 2019-12-27 00:42:50 +05:30
Sayan Goswami 2a9fde7e7e Add Schaffer N.1 function 2019-12-27 00:19:08 +05:30
Sayan Goswami a352f60755 Convert vector<double> to arma::vec 2019-12-26 23:36:33 +05:30
Sayan Goswami e150f635d4 Fix check for objectiveNumber 2019-12-26 20:44:58 +05:30
Sayan Goswami 127953bd2f Remove Evaluate callback 2019-12-26 20:22:15 +05:30
Sayan Goswami c417b08b38 Add callbacks, remove unused parameters 2019-12-26 20:10:25 +05:30
Sayan Goswami 80a9a12d3a Remove unused typedef 2019-12-26 19:11:41 +05:30
Sayan Goswami 274b0d74d0 Fix linker, compiler errors & add logging 2019-12-26 19:08:01 +05:30
Sayan Goswami ceeb7cc210 Add tests for NSGA2 2019-12-26 17:09:59 +05:30
Sayan Goswami 4eed69a456 Implement NSGA2 optimizer 2019-12-26 17:09:02 +05:30
Rahul Prabhu 46856b09f7 Fixed add_test 2019-12-26 11:09:22 +05:30
Rahul Prabhu 53ee9f5360 Fixed version 2019-12-25 18:59:52 +05:30
Rahul Prabhu 1812812296 Cut down a bit, and some more documentation 2019-12-25 18:57:26 +05:30
Rahul Prabhu 5c1eb89528 It works 2019-12-25 18:25:20 +05:30
Ryan Curtin 753e3cd4a3 Add names of releases. 2019-12-24 23:30:53 -05:00
Ryan Curtin 3807cec72a Update HISTORY for release. 2019-12-24 23:06:48 -05:00
Ryan Curtin f1e8084a83 Update and release version 2.11.0. 2019-12-24 23:03:35 -05:00
Ryan Curtin 22269e4e4e Merge pull request #138 from zoq/Lookahead
Lookahead Optimizer: k steps forward, 1 step back
2019-12-24 22:47:40 -05:00
Sayan Goswami 79150db4ce Add constructor, FastNonDominatedSort and Dominates (pareto dominance) 2019-12-25 00:59:50 +05:30
Ryan Curtin 540b00ce0b Merge pull request #148 from say4n/readme
Add build/test instructions
2019-12-24 11:50:44 -05:00
Marcus Edel 13291c5505 Merge branch 'master' into Lookahead 2019-12-24 14:38:50 +01:00
Marcus Edel 67dce9f5fb Reword the info if the base optimzer does not have a definition of MaxIterations() and update the function types documentation. 2019-12-24 14:37:05 +01:00
Sayan Goswami 1c2304c027 Update CONTRIBUTING.md
Add build instructions for Arch, fix typo
2019-12-24 13:10:46 +05:30
Ryan Curtin 9efcda94ec Merge pull request #137 from zoq/AdaBound
AdaBound + AMSBound
2019-12-23 21:26:50 -05:00
Marcus Edel 03dbd6a1fc Adjust step size (Lookahead test). 2019-12-24 02:32:49 +01:00
Marcus Edel 64af8f9ab5 Optimize test settings to make the Lookahead test suite more stable. 2019-12-24 00:14:26 +01:00
Marcus Edel 24ab54dc88 Add another constructor that uses a default base optimizer instance. 2019-12-23 22:33:34 +01:00
Marcus Edel d560239f41 Update the defaults for the Lookahead optimzer. 2019-12-23 22:32:16 +01:00
Sayan Goswami ff381b912e Update CONTRIBUTING.md
Add cmake to dependencies, add instructions for Fedora/RHEL/CentOS, add Homebrew package, remove typo (extra backticks)
2019-12-23 15:31:30 +05:30
Sayan Goswami 00660aa99c Update release script
Add updated CONTRIBUTING.md on new release to git
2019-12-23 15:29:08 +05:30
Marcus Edel e076f8b7ab Add parameter (resetPolicy) which resets the update policy before the optimizer call. 2019-12-22 21:09:41 +01:00
Marcus Edel 6e61946f6c Move HasBatchSize() and HasMaxIterations() from the callbacks to the functions traits. 2019-12-22 21:08:28 +01:00
Marcus Edel 84f94e98a9 Fix doxygen bibtex block (start with @code and end with @endcode). 2019-12-22 21:06:57 +01:00
Sayan Goswami 99478eab82 Add NSGA2 (WIP) 2019-12-22 17:47:23 +05:30
Sayan Goswami dbd9fc0c91 Add name to list of contributors 2019-12-21 10:46:16 +05:30
Sayan Goswami ce4aede3b0 Auto update CONTRIBUTING.md test instructions on new release 2019-12-18 20:09:20 +05:30
Sayan Goswami 0438bea03c Update CONTRIBUTING.md
Use semantic version number of latest release instead of x.x.x placeholders
2019-12-18 20:07:05 +05:30
Sayan Goswami b5043a142e Update CONTRIBUTING.md
Remove lapack from dependencies, update archive to use semver instead of latest, default to make without j flag, fix typo.
2019-12-18 11:31:22 +05:30
Sayan Goswami 245aa48c71 Migrate build/test instructions to CONTRIBUTING.md 2019-12-16 12:12:18 +05:30
Sayan Goswami 6392f38133 Add cmake to dependencies 2019-12-15 20:10:17 +05:30
Sayan Goswami 30e2cd499f Update README: add build/test instructions 2019-12-15 19:42:41 +05:30
Marcus EdelandRyan Curtin b447a0cdbe Minor spelling fix.
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2019-12-14 23:36:09 +01:00
Marcus Edel 1b98ce8394 Add Lookahead to the optimizer function documentation. 2019-12-14 18:53:43 +01:00
Marcus Edel 05d02a168d Update history. 2019-12-14 18:53:05 +01:00
Marcus Edel 9012983751 Merge branch 'master' into Lookahead 2019-12-14 18:47:28 +01:00
Marcus EdelandRyan Curtin 4471cdf0a0 Use the correct update policy.
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2019-12-14 01:11:35 +01:00
Marcus Edel 5f9d5147ee Update history. 2019-12-13 22:21:35 +01:00
Marcus Edel 5384c73117 Add AdaBound and AMSBound to the optimizer function documentation. 2019-12-13 22:20:04 +01:00
Marcus Edel 1d45614f41 Merge branch 'master' into AdaBound 2019-12-13 22:12:09 +01:00
Ryan Curtin 7557fe83ac Fix history. 2019-12-13 13:41:08 -05:00
Ryan Curtin 892b4c4892 Update and release version 2.10.5. 2019-12-13 13:01:40 -05:00
Ryan Curtin e4b839732e Merge pull request #145 from rcurtin/de-doc-fix
Update static check terms to match documentation
2019-12-13 10:00:55 -08:00
Marcus Edel 681cef818f Adjust parameter order and fix indentation. 2019-12-12 23:05:47 +01:00
Marcus EdelandRyan Curtin 1dff1c78ef Use camel casing for the parameter description.
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2019-12-12 22:56:08 +01:00
Marcus EdelandRyan Curtin 27c310c2b7 Minor indentation fix.
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2019-12-12 22:55:29 +01:00
Marcus EdelandRyan Curtin c84ba3cd16 Minor spelling fix.
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2019-12-12 22:54:36 +01:00
Marcus EdelandRyan Curtin 605a7f4eb6 Use camel casing for the parameter description.
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2019-12-12 22:54:11 +01:00
Ryan Curtin 6f690b2e52 Merge branch 'master' into de-doc-fix 2019-12-11 17:22:40 -05:00
Marcus Edel 18ce994bfd Merge pull request #147 from rcurtin/fix-using
Remove 'using namespace std' from problems.
2019-12-11 23:00:15 +01:00
Ryan Curtin f2cf863718 Make sure std::abs() gets used. 2019-12-10 19:26:47 -05:00
Ryan Curtin 64d69c8e5e Update HISTORY. 2019-12-10 13:01:53 -05:00
Ryan Curtin d51b5c4f11 Remove 'using namespace std' from problems. 2019-12-10 11:03:10 -05:00
Ryan Curtin 21981a6eb7 Update HISTORY. 2019-12-10 01:08:51 -05:00
Ryan Curtin 4911341aea Change Decomposable->Separable, and NonDifferentiable->Arbitrary. 2019-12-10 01:04:12 -05:00
Marcus Edel 9b730c7212 Move the latest update into a new block. 2019-12-05 19:24:40 +01:00
Marcus Edel d7fda77f0f Update history. 2019-12-05 19:19:43 +01:00
Marcus Edel d70b12a296 Merge pull request #143 from barak/master
32-bit safety in big random number.
2019-12-05 19:16:42 +01:00
Barak A. Pearlmutter 176fbef576 32-bit safety in big random number
This patch fixes a compile-time warning and test case failure on
32-bit architectures, including i386.

    cd /<<PKGBUILDDIR>>/obj-i686-linux-gnu/tests && /usr/bin/c++   -I/<<PKGBUILDDIR>>/include  -g -O2 -fdebug-prefix-map=/<<PKGBUILDDIR>>=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fopenmp -Wall -Wpedantic -Wunused-parameter   -std=gnu++11 -o CMakeFiles/ensmallen_tests.dir/cmaes_test.cpp.o -c /<<PKGBUILDDIR>>/tests/cmaes_test.cpp
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp: In function ‘void ____C_A_T_C_H____T_E_S_T____46()’:
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp:397:28: warning: unsigned conversion from ‘long long int’ to ‘size_t’ {aka ‘unsigned int’} changes value from ‘10000000000’ to ‘1410065408’ [-Woverflow]
      397 |   StandardSGD s(0.0003, 1, 10000000000, -10);
	  |                            ^~~~~~~~~~~
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp: In function ‘void ____C_A_T_C_H____T_E_S_T____54()’:
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp:473:28: warning: unsigned conversion from ‘long long int’ to ‘size_t’ {aka ‘unsigned int’} changes value from ‘10000000000’ to ‘1410065408’ [-Woverflow]
      473 |   StandardSGD s(0.0003, 1, 10000000000, -100, true);
	  |                            ^~~~~~~~~~~

    ...

    Test project /<<PKGBUILDDIR>>/obj-i686-linux-gnu
	Start 1: ensmallen_tests
    1/1 Test #1: ensmallen_tests ..................***Failed  5163.40 sec

The SGD maxIterations parameter to the SGD routine is stored in a
size_t. Arguably inappropriate; it belongs in something guaranteed to
be at least 64 bits, perhaps long long int.

Be that as it may, a test routine was passing a number meant to be so
large it would not be exceeded, to test a mechanism that should
terminate the optimization early. The large number chosen was
10000000000, ten billion, which exceeds the maximum value of an
unsigned 32-bit integer of 4 billion plus change.

This patch changes the maxIterations parameter passed to two billion,
2000000000, which is both sufficiently large for the purposes here and
safely below the maximum value for a 32-bit signed integer.
2019-12-05 14:30:42 +00:00
Ryan Curtin a7798de79f Update and release version 2.10.4. 2019-11-18 19:21:38 -05:00
Ryan Curtin 4709993928 Update HISTORY for release. 2019-11-18 19:21:30 -05:00
Ryan Curtin 047dadd295 Merge branch 'jeffin143-introduce-collapse' 2019-11-18 19:20:34 -05:00
Ryan Curtin 7ec3a746d9 Update history. 2019-11-18 19:20:21 -05:00
Ryan Curtin 8f6a907f9a Merge branch 'introduce-collapse' of https://github.com/jeffin143/ensmallen into jeffin143-introduce-collapse 2019-11-18 19:19:54 -05:00
jeffin samandRyan Curtin 3e6dbe298a Update doc/optimizers.md
Co-Authored-By: Ryan Curtin <ryan@ratml.org>
2019-11-18 21:14:21 +05:30
Marcus Edel 91e5abbd7b Merge pull request #141 from robertohueso/master
Add optional test building.
2019-11-10 14:38:43 +01:00
Roberto Hueso Gomez 91936ebbab Update history 2019-11-09 22:07:13 +01:00
Roberto Hueso Gomez f97b2b9004 Add optional test building
Add a new option to choose whether to build tests or not.
2019-11-09 03:37:44 +01:00
jeffinsam 72b0a1fd1f Removing <p> tage 2019-11-07 14:46:20 +05:30
jeffinsam 71574b6bb3 make sample code collapsible 2019-11-01 12:57:27 +05:30
Marcus Edel 0464db9acf Return model coordinates if the optimization process terminates. 2019-09-29 20:02:45 +02:00
Marcus Edel 7c37b614e3 Update and release version 2.10.3. 2019-09-26 20:37:16 +02:00
Marcus Edel a1d2705500 Update for latest release. 2019-09-26 20:23:41 +02:00
Marcus Edel d15772b268 Merge pull request #135 from zoq/PSGDFix
ParallelSGD omp fix.
2019-09-26 16:19:26 +02:00
Marcus Edel 59f62b2200 Use batch size from the base optimizer. 2019-09-26 00:20:06 +02:00
Marcus Edel 399793bd57 Fix reorder issue. 2019-09-25 22:08:23 +02:00
Marcus Edel 7e95a4fc1a Update history. 2019-09-25 19:13:09 +02:00
Marcus Edel 39af8fbf5e Use the correct function name. 2019-09-25 19:11:48 +02:00
Marcus Edel 54587897d4 Add Lookahead optimizer test cases. 2019-09-25 19:06:37 +02:00
Marcus Edel 198ac2cda1 Add Lookahead to the optimizer documentation. 2019-09-25 19:06:08 +02:00
Marcus Edel 60450079f7 Add Lookahead optimizer. 2019-09-25 19:05:41 +02:00
Marcus Edel 1f94efdf9b Clarify comment. 2019-09-25 19:02:55 +02:00
Marcus Edel 564a37d978 Merge branch 'master' into PSGDFix 2019-09-25 18:56:43 +02:00
Marcus Edel 6f3c6b6f7a Update history. 2019-09-25 18:55:41 +02:00
Marcus Edel 466bc012a4 Merge branch 'master' into AdaBound 2019-09-21 18:45:21 +02:00
Marcus Edel 24487032e1 Update HISTORY.md (new optimizer AdaBound + AMSBound). 2019-09-21 18:44:30 +02:00
Marcus Edel 4c83c9de44 Add AdaBound and AMSBound to the optimizer documentation. 2019-09-21 18:41:30 +02:00
Marcus Edel 84073d3575 Add AdaBound and AMSBound test cases. 2019-09-21 18:32:00 +02:00
Marcus Edel b13228283b Add AdaBound and AMSBound. 2019-09-21 18:31:39 +02:00
favre49 0f9dc82e6d Merge pull request #136 from rcurtin/lbfgs-scaling-factor
Check L-BFGS scaling factor for convergence
2019-09-18 20:39:43 +05:30
Marcus Edel 649010cc60 Merge pull request #134 from barak/master
Best practice: set bash option to terminate shell upon expected error.
2019-09-16 21:58:15 +02:00
Ryan Curtin 053c79c98d Update history. 2019-09-16 11:39:56 -04:00
Ryan Curtin 693fd2bd27 A scaling factor of 0 means we should terminate. 2019-09-16 11:38:52 -04:00
Marcus Edel 741dab09ad Limit the number of iterations and reduce the number of test functions. 2019-09-15 00:32:19 +02:00
Marcus Edel ddc16899a4 Use named omp critical to avoid some strange deadlock. 2019-09-15 00:31:30 +02:00
Barak A. Pearlmutter 7c0215e172 best practice: set bash option to terminate shell upon expected error 2019-09-13 12:43:00 +01:00
Ryan Curtin cbd149c0f4 Update and release version 2.10.2. 2019-09-11 18:01:13 -04:00
Ryan Curtin ac745b0d4f Update for latest release. 2019-09-11 18:00:58 -04:00
Ryan Curtin c6d076f8b0 Merge branch 'rcurtin-rel-tools' 2019-09-11 17:59:11 -04:00
Ryan Curtin 11d2a9a7c6 Merge branch 'rel-tools' of https://github.com/rcurtin/ensmallen into rcurtin-rel-tools 2019-09-11 17:59:04 -04:00
Ryan Curtin 222ce84a9b Merge pull request #133 from conradsnicta/fix-version-check
ensure armadillo version check uses the same numbers as CMakeLists.txt
2019-09-11 10:03:02 -04:00
Conrad Sanderson 166b80271d ensure armadillo version check uses the same numbers as CMakeLists.txt 2019-09-11 07:19:33 +02:00
Ryan Curtin a3e9ab6956 Update and release version 2.10.1. 2019-09-10 20:29:36 -04:00
Ryan Curtin ef91cd751b Update and release version 1.12.1. 2019-09-10 20:27:38 -04:00
Ryan Curtin f42613faeb Update history. 2019-09-10 20:27:31 -04:00
Ryan Curtin 083a9a63a0 Better compatibility. 2019-09-10 11:51:52 -04:00
Ryan Curtin 5a87d5a16d Merge pull request #131 from rcurtin/compat-fixes
Minor compatibility fixes for ensmallen 1.x
2019-09-09 17:38:50 -04:00
Ryan Curtin 0e542f6d00 Oops, forgot to include this fix. 2019-09-08 02:09:45 -04:00
Ryan Curtin 4b0bf47155 Set default type. 2019-09-08 01:37:23 -04:00
Ryan Curtin ccc0e11049 Minor compatibility fixes. 2019-09-08 00:47:30 -04:00
Marcus Edel 2554e32bf1 Merge pull request #129 from coatless/patch-2
Switch mlpack with ensmallen in callback docs.
2019-09-07 19:53:47 +02:00
James J Balamuta 73644c6a4f ensmallen not mlpack (h/t @eddelbuettel) 2019-09-07 12:20:28 -05:00
Ryan Curtin 5f4cc9fc26 A couple more notes about the release process. 2019-09-07 11:37:22 -04:00
Ryan Curtin 06067c4635 Add release details. 2019-09-07 11:34:38 -04:00
Ryan Curtin e921973658 Update and release version 2.10.0. 2019-09-07 11:14:01 -04:00
Ryan Curtin 687b5aa8a8 Update for release. 2019-09-07 11:13:57 -04:00
Ryan Curtin d0cd0da2f5 Merge pull request #127 from conradsnicta/fast-solve
use fast solve
2019-09-06 22:15:28 -04:00
conrad be9b78809d use fast solve 2019-09-05 11:49:23 +10:00
Marcus Edel 45efb3e5d0 Merge pull request #119 from zoq/cb
Callbacks + Templated Optimize().
2019-09-04 11:47:03 +02:00
Marcus Edel 9ea1acb394 Use exact objective to increase the test tolerance. 2019-08-31 23:25:03 +02:00
Marcus Edel dd99eed7da Make sure the progress bar has all necessary functions. 2019-08-31 23:16:08 +02:00
Marcus Edel 21d43a8627 Address code review comments (documentation, simplification). 2019-08-31 21:22:17 +02:00
Ryan Curtin e399775c92 Use operator() not operator[] for bounds checks.
Signed-off-by: Marcus Edel <marcus.edel@fu-berlin.de>
2019-08-31 19:29:44 +02:00
Marcus Edel bebb8b162c Adjust BBSGD test setting (large initial batch size). 2019-08-26 19:14:26 +02:00
Marcus Edel b3481608b0 Slightly increase the number of particle. 2019-08-21 22:18:22 +02:00
Marcus Edel 01b0ea99b9 Adjust test settings BBSGD, AdaDelta. 2019-08-19 22:16:31 +02:00
Marcus Edel 79b77c7db9 Adjust optimizer test settings to be more prone to errors. 2019-08-18 18:07:17 +02:00
Marcus Edel 92daf65c71 Merge remote-tracking branch 'origin/master' into cb 2019-08-18 13:57:09 +02:00
Ryan Curtin 63e4d460e5 Add new history block. 2019-08-12 14:08:59 -04:00
Ryan Curtin fd51ff1a51 Update and release version 1.16.2. 2019-08-12 14:07:42 -04:00
Ryan Curtin f182766174 Update history. 2019-08-12 14:07:28 -04:00
Ryan Curtin 95ba9313d2 Merge pull request #126 from rcurtin/pso-fix-2
Fix incorrect return type for PSO.
2019-08-12 14:06:07 -04:00
Marcus Edel 41d67ae0dc Update bound type from arma::vec to arma::mat in the PSO documentation. 2019-08-11 22:56:22 +02:00
Marcus Edel c154978f66 Remove debug sleep. 2019-08-11 22:53:25 +02:00
Marcus Edel 467b7c5718 Per epoch progress bar. 2019-08-11 22:50:43 +02:00
Marcus Edel faf0926c92 Add epoch begin/end callback to optimizer. 2019-08-11 22:50:19 +02:00
Marcus Edel d767d2312a Use matrix instead of vector for bound type. Enable matrix optimization. 2019-08-11 22:48:44 +02:00
Ryan Curtin c2c4810c5c Update HISTORY. 2019-08-11 12:53:34 -04:00
Ryan Curtin 4a4e92df1d Fix incorrect return type. 2019-08-11 12:36:55 -04:00
Ryan Curtin bfd51b00f6 Add block for next release. 2019-08-11 10:51:12 -04:00
Ryan Curtin 549ae4f6fd Update and release version 1.16.1. 2019-08-11 10:49:14 -04:00
Ryan Curtin 9627721f41 Update HISTORY for a release. 2019-08-11 10:49:05 -04:00
Ryan Curtin 18a5cce036 Merge pull request #125 from rcurtin/history-fix
Add version names and use Markdown links in HISTORY.md
2019-08-11 10:47:30 -04:00
Ryan Curtin af2a890698 Merge pull request #124 from zoq/PSOBoundTypeFix
Fix PSO bound type
2019-08-11 10:47:18 -04:00
Marcus Edel f387fad907 Minor style fixes (remove trailing spaces). 2019-08-11 13:52:21 +02:00
Marcus Edel 3f1d35efea Fix PSO bound type. 2019-08-11 13:51:57 +02:00
Ryan Curtin 1710e00774 Add version names and use Markdown links. 2019-08-09 18:36:27 -04:00
Marcus Edel 0d1fa1cea7 Templated problems. 2019-08-09 22:34:16 +02:00
Ryan Curtin b46986d134 Update and release version 1.16.0. 2019-08-09 09:15:14 -04:00
Ryan Curtin 89bf4057a5 Update history. 2019-08-09 09:14:52 -04:00
Marcus Edel f236c83738 Add PSO parameter/template description. 2019-08-08 22:36:07 +02:00
Ryan Curtin ca48812ca1 Merge pull request #109 from walragatver/exact-objective
Providing option to calculate exact objective
2019-08-07 22:11:07 -04:00
Marcus Edel 591f341aa4 Templated PSO Optimize function. 2019-08-05 17:29:59 +02:00
Marcus Edel fa10557390 Minor style fixes. 2019-08-05 17:28:49 +02:00
Marcus Edel a3656bf649 Merge remote-tracking branch 'origin/master' into cb 2019-08-03 22:59:54 +02:00
Marcus Edel 98d0913cf7 Move callbacks. 2019-08-03 22:01:30 +02:00
Marcus Edel d4b3680ede Add StepTaken callback to optimizer. 2019-08-03 21:37:20 +02:00
Marcus Edel 29148a040d Add StepTaken callback function. 2019-08-03 21:36:16 +02:00
Marcus Edel 77f345baae Remove not implemented overload. 2019-08-03 14:24:01 +02:00
Marcus Edel 948fba6689 Merge pull request #86 from SuryodayBasak/pso-dev
Introducing LBest PSO for Unconstrained Optimization Problems.
2019-08-01 20:17:19 +02:00
walragatver 6fd7397d69 Modify Documentation. 2019-08-01 20:30:09 +05:30
walragatver c1a7fa604b Add exact objective at last position. 2019-08-01 00:24:54 +05:30
Suryoday Basak e63796144e Referencing in pso.hpp 2019-07-28 15:44:00 -05:00
Suryoday Basak ac1a974e21 Reverting 2019-07-27 20:15:30 -05:00
Suryoday Basak 7e82920928 Fixing minor syntax problem in pso.hpp 2019-07-27 20:02:17 -05:00
Suryoday Basak 8bef77be13 Updating documentation. 2019-07-24 19:59:38 -05:00
Suryoday Basak 3def96e254 Updating PSO documentation with template types 2019-07-24 08:29:41 -05:00
Suryoday Basak 9cec0c9314 Fixing grammar in pso documentation. 2019-07-24 08:19:37 -05:00
Suryoday Basak 2713459988 Including documentation and updating history. 2019-07-24 08:15:57 -05:00
walragatver 678a07d19b Fix documentation issue. 2019-07-20 23:21:08 +05:30
walragatver 1806ba9e91 Rebasing and fixing warnings. 2019-07-20 23:21:08 +05:30
walragatver 7982124e7d Modify Qhsgd and QHAdam. 2019-07-20 23:18:57 +05:30
walragatver 776c9e5842 Modify history and documentation. 2019-07-20 23:15:34 +05:30
walragatver debf5cf8a5 Modify comments. 2019-07-20 23:15:34 +05:30
walragatver 23940c6aaf Revert changes to cne, spsa, scd. 2019-07-20 23:15:34 +05:30
walragatver a6c2062c90 Modify spsa and svrg. 2019-07-20 23:15:34 +05:30
walragatver 3a64fedd12 Modify sarah, scd and spalera_sgd. 2019-07-20 23:15:34 +05:30
walragatver c6bfe06b9a Modify cne, eve and katyusha. 2019-07-20 23:15:34 +05:30
walragatver 7b0ea9909f Modify bigbatch_sgd, standardSGD. 2019-07-20 23:15:34 +05:30
walragatver 4e575f7718 Modify sgdr and snapshot_sgdr. 2019-07-20 23:15:34 +05:30
walragatver 048cdba028 Modify Adam, momentum, nestrov_momentum optimizer. 2019-07-20 23:15:34 +05:30
walragatver a698919e0f Mofify ada_delta, ada_grad, ftml, padam optimizers. 2019-07-20 23:15:34 +05:30
walragatver bce0b3fa28 Modify RMSprop and SNorm. 2019-07-20 23:15:34 +05:30
walragatver 6d4a63914c Modify Swats and WGrad. 2019-07-20 23:15:34 +05:30
Suryoday Basak 8084603b6f Removing Schaffer Function N4 test for the time being 2019-07-19 08:38:10 -05:00
Suryoday Basak 023894725b Changing data used for values of infinity 2019-07-19 03:49:52 -05:00
Suryoday Basak c945f69d69 reducing number of lines in default_init 2019-07-19 03:41:02 -05:00
Suryoday Basak b5e88de72b Formatting comments with proper spacing and grammar. 2019-07-17 18:27:48 -05:00
Suryoday Basak 4763d3c466 Testing PSO with lowerBound and upperBound initialized as double 2019-07-17 01:50:07 -05:00
Suryoday Basak b33795bfb7 Removing reference operations in lbest_update 2019-07-17 01:32:43 -05:00
Suryoday Basak e511aee401 Including good comments for parameter documentation 2019-07-17 00:13:17 -05:00
Suryoday Basak a931460129 Implementing lookback-horizon using std::queue 2019-07-16 02:58:18 -05:00
Suryoday Basak ff016494f5 Trying to tune PSO to work well with SchafferFunctionN4 function 2019-07-15 04:32:49 -05:00
Suryoday Basak bbabf7354f Implemented horizon-lookback method for PSO termination 2019-07-15 04:10:29 -05:00
Marcus Edel ffcb717f1b Use the correct path. 2019-07-07 22:47:01 +02:00
Marcus Edel 823b074cbc Help travis to deduce the format. 2019-07-07 22:44:07 +02:00
Suryoday Basak c081477681 Implemented constructor of PSOType that takes upper and lower bounds of type double 2019-07-07 01:19:01 -05:00
Marcus Edel e3b0918a55 Install xz-utils (travis build). 2019-07-06 21:22:45 +02:00
Marcus Edel e9b2b0ea38 Let's see if tar can figure out what format this is. 2019-07-06 19:03:42 +02:00
Marcus Edel 9e52a67c2f Bump minimum armadillo version. 2019-07-06 17:59:10 +02:00
Marcus Edel 1736d18558 Bump minimum version (armadillo) for the travis build. 2019-07-06 17:57:55 +02:00
Marcus Edel 2eda0cb86b Use the specefied iterations for the CNE tests. 2019-07-06 17:47:18 +02:00
Suryoday Basak a341ff6113 Including the schaffer function N4 and N2 tests 2019-07-05 02:57:56 -05:00
Suryoday Basak e58be41c80 Updating the cross-in-tray, beale, and ackley functions with initial populations far from the optimum 2019-07-05 02:09:11 -05:00
Suryoday Basak a23ba84d2f Removing erroneous member functions from default_init and including asset statements to check the dimensionality of upperBound and lowerBound 2019-07-05 01:46:52 -05:00
Suryoday Basak 4d70fe8e32 Changing API to include lowerBound and upperBound as params of arma::vec type 2019-07-05 01:28:55 -05:00
Suryoday Basak 3e5d13f451 Written PSO tests for cross-in-tray, beale, and ackley functions 2019-07-03 12:22:49 -05:00
Suryoday Basak 593d53beb8 Merge branch 'master' into pso-dev
Merging with master to get the new test functions for non-convex optimization.
2019-07-03 12:01:51 -05:00
Suryoday Basak 6c32fbf3a7 Fixed problems with argument list 2019-07-03 00:12:50 -05:00
Suryoday Basak 914ef1124b Fixing templates 2019-07-02 23:00:15 -05:00
Suryoday Basak 8f3f1a36fc Fixing includes 2019-07-02 22:49:45 -05:00
Suryoday Basak 9ded40c6e0 Removing feasible_init for PSO, for now 2019-07-02 22:38:09 -05:00
Suryoday Basak 59b41595d4 Fixing style for lbest_update again 2019-07-02 22:11:58 -05:00
Suryoday Basak e701fc4bda Style fixes for lbest_update 2019-07-02 22:06:46 -05:00
Suryoday Basak cb52f76872 Formatting code for pso and pso_impl 2019-07-02 21:31:37 -05:00
Marcus Edel 04b6651b72 Merge branch 'master' into cb 2019-06-29 21:04:20 +02:00
Marcus Edel 9d2a5f07c6 Minor style fixes (comment style, indentation). 2019-06-23 15:16:19 +02:00
Marcus Edel 9c6258f697 Merge pull request #117 from SuryodayBasak/test-func-dev
Including new test functions.
2019-06-23 14:35:02 +02:00
Marcus Edel a2fa74aba3 Move terminate paramter inside the Optimize() function. 2019-06-21 21:23:03 +02:00
Ryan Curtin ca92d0e3b9 Merge pull request #118 from rcurtin/test-cleanup
Clean up tests (runtime and failure probability)
2019-06-21 13:01:41 -04:00
Suryoday Basak 0338dd2def Including error message for assertion on the value of phi 2019-06-20 23:31:34 -05:00
Suryoday Basak 60bf91ba92 Fixing typo 2019-06-20 16:44:38 -05:00
Suryoday Basak c006a863e5 Including documentation in code for PSO lbest policy 2019-06-20 16:16:25 -05:00
Marcus Edel b5633b83b7 Pass the correct objective. 2019-06-19 22:51:32 +02:00
Marcus Edel 532f9fa0db Add callbacks documentation. 2019-06-19 22:51:32 +02:00
Marcus Edel 59c5fef643 Test optimizers against Callback functions. 2019-06-19 22:51:32 +02:00
Marcus Edel 92b7a20952 Add callback functions to optimizers. 2019-06-19 22:51:32 +02:00
Marcus Edel 830dca2263 Revert function callbacks wrapper. 2019-06-19 22:51:32 +02:00
Marcus Edel 066bafa2fb Rename timer 'time' -> 'duration' and fix minor style issues (unused parameter). 2019-06-19 22:51:32 +02:00
Marcus Edel 7fd7b2a779 Add armadillo subview trait. 2019-06-19 22:51:32 +02:00
Marcus Edel 090f4e783b Add armadillo type traits. 2019-06-19 22:51:32 +02:00
Marcus Edel 2266177ffe Add callbacks tests. 2019-06-19 22:51:32 +02:00
Marcus Edel 9bf92cff01 Add optimizer type to function class. 2019-06-19 22:51:32 +02:00
Marcus Edel c03ed4a8d7 Include callbacks header files. 2019-06-19 22:51:32 +02:00
Marcus Edel f11bbf1eaf Add early stop at min loss callback. 2019-06-19 22:51:32 +02:00
Marcus Edel fd9bae5980 Add print loss callback. 2019-06-19 22:51:32 +02:00
Marcus Edel d38d02dd56 Add progress bar callback. 2019-06-19 22:51:32 +02:00
Marcus Edel 15ca11b7ec Add store best coordinates callback. 2019-06-19 22:51:32 +02:00
Marcus Edel 8c338dd4e4 Add timer stop callback. 2019-06-19 22:51:32 +02:00
Suryoday Basak bcc86542bf Style fixes 2019-06-19 01:21:26 -05:00
Suryoday Basak ae295fd75f Removing heuristic used to replace the worst particle in the swarm 2019-06-16 23:44:08 -05:00
Suryoday Basak 377edc237d Adding description in pso_impl.hpp 2019-06-14 23:46:26 -05:00
Suryoday Basak 6cdf03b0f3 Including a reference to the function type definitions, plus some style fixes 2019-06-14 18:58:59 -05:00
Suryoday Basak ac602adad4 Changing the description of 'Optimize' 2019-06-14 18:35:00 -05:00
Suryoday Basak 0bbc8858fd Including detailed description of PSO 2019-06-14 14:49:14 -05:00
Suryoday Basak 7cbc38936d Editing tests for schaffer n4, holder table and cross-in-tray 2019-06-12 06:51:51 -05:00
Suryoday Basak cb98258824 Fixing travis yml manually 2019-06-06 04:04:05 -05:00
Suryoday Basak fad6416348 Trying to sync dot travis dot yml again 2019-06-04 21:33:44 -05:00
Suryoday Basak c766f00b2c Trying to sync travis yml 2019-06-04 20:07:15 -05:00
Ryan Curtin 00e26806b4 Recomment accidentally-committed random seed setting. 2019-06-04 11:19:40 -04:00
Ryan Curtin e51b5c0d2e Remove unnecessary parts of file (or some of them at least). 2019-06-04 11:19:16 -04:00
Ryan Curtin 01f3dd8d04 Fix use of ElemType---could this have caused slow convergence? 2019-06-04 11:18:58 -04:00
Ryan Curtin 5454e99a14 Tune test to prevent failures. 2019-06-04 11:18:47 -04:00
Ryan Curtin e8b0aa2c13 Use ElemType. 2019-06-04 11:18:39 -04:00
Suryoday Basak 34a81c6085 Style fixes for Schaffer functions N.2 and N.4 2019-06-03 21:42:05 -05:00
Suryoday Basak a210f1e5b9 Style fixes for himmelblau, three-hump camel, cross-in-tray, and holder table functions 2019-06-03 21:01:45 -05:00
Suryoday Basak 2a82e4bd9e Goldstein-Price style fix; changing Levi to Levy 2019-06-03 18:08:12 -05:00
Suryoday Basak 71a8725cee Fixing spacing in Beale function 2019-06-03 17:09:10 -05:00
Suryoday Basak c6299b167f Fixing code style of Beale function 2019-06-03 17:06:50 -05:00
Suryoday Basak 21b8152d64 Fixing code style of ackley_function 2019-06-03 16:11:34 -05:00
Ryan Curtin 09db59668c Merge remote-tracking branch 'origin/master' into templated_optimize 2019-06-02 21:31:02 -04:00
Suryoday Basak 9c494b3706 Tweaking CNE test for Schaffer Function N4 2019-06-01 22:20:44 -05:00
Suryoday Basak 8fa5ba3d85 Adding tests for Himmelblau function, Three-hump camel, and Schaffer Function N.2 for CNE 2019-06-01 22:02:32 -05:00
Suryoday Basak 6646650e77 Fixing reorder warning in Ackley function 2019-06-01 21:38:16 -05:00
Suryoday Basak 04b6065853 Fixing travis yml file 2019-06-01 21:02:59 -05:00
Suryoday Basak 65e1d0b981 Adding Goldstein-Price and Levi function tests for CNE 2019-06-01 20:37:01 -05:00
Suryoday Basak b6e521643f Adding Ackley and Beale function tests for CNE 2019-06-01 20:18:32 -05:00
Suryoday Basak 1c07d34857 Included tests for cross-in-tray, holder table, and schaffer n4 functions 2019-06-01 18:14:44 -05:00
Suryoday Basak 03b9cde182 Including a test for Schaffer Function N.2 2019-06-01 17:05:30 -05:00
Suryoday Basak 6143c58428 Merge branch 'test-func-dev' of https://github.com/SuryodayBasak/ensmallen into test-func-dev
Resolving conflicts with .travis.yml
2019-06-01 16:31:01 -05:00
Suryoday Basak 05b58a659d Merging with upstream 2019-06-01 12:23:19 -05:00
Suryoday Basak 0b255dbc41 Merging remote for travis.yml 2019-06-01 12:18:18 -05:00
Marcus Edel 4425e610d7 Merge branch 'master' into test-func-dev 2019-06-01 13:46:25 +02:00
Suryoday Basak 2bfad41aa1 Including a test for Three-hump camel function 2019-06-01 03:41:56 -05:00
Suryoday Basak d5d92f3490 Including a test for Three-hump camel function 2019-06-01 03:41:29 -05:00
Suryoday Basak a22cd098aa Including a test for Himmelblau function 2019-06-01 03:26:44 -05:00
Suryoday Basak f3f5bf8abd Including a test for the Levi Function N.13 2019-06-01 02:51:39 -05:00
Suryoday Basak 4cea37462c Tweaking travis build options 2019-06-01 02:12:20 -05:00
Suryoday Basak d643b6b8dc Changing name of variable c in Ackley test function 2019-06-01 01:26:23 -05:00
Suryoday Basak af1ba2da6a Including test for Goldstein Price function 2019-06-01 01:08:52 -05:00
Ryan Curtin 984060cf5b Better strategy for handling non-finite v. 2019-06-01 00:39:19 -04:00
Suryoday Basak be07e420e7 Correcting the gradient expression for the BealeFunction and adding a test for it 2019-05-31 23:32:27 -05:00
Suryoday Basak e0def7d0cb Fixing gradient of Ackley function and including Ackley test for Adam 2019-05-31 04:08:50 -05:00
Suryoday Basak 3c25045b5c Including Schaffer function N.2 2019-05-30 02:51:40 -05:00
Suryoday Basak 61df4373a8 Including Schaffer function N.4 2019-05-29 22:58:49 -05:00
Suryoday Basak d086f03160 Including the holder table function 2019-05-29 16:48:28 -05:00
Suryoday Basak 55a1e8127d Including Cross-In-Tray function without gradient 2019-05-29 15:29:16 -05:00
Ryan Curtin 756a605be5 Update history. 2019-05-27 17:05:11 -04:00
Ryan Curtin 7a9de32f80 Fix compilation warning. 2019-05-27 17:01:40 -04:00
Ryan Curtin 8e8db3488f Handle case where estimated curvature is NaN.
Also tune the tests a little bit.
2019-05-27 17:00:44 -04:00
Ryan Curtin e9f7cf3135 Tune test to reduce failures. 2019-05-27 17:00:23 -04:00
Ryan Curtin 6a37a17f4a Use simpler CNE test functions to save time.
(This can save a *huge* amount of time!  These tests used to take almost 40
minutes.)
2019-05-27 16:59:58 -04:00
Ryan Curtin c4a1e3d680 Clarify documentation (and make it 80 characters). 2019-05-27 16:59:42 -04:00
Suryoday Basak 4d0daa3399 Including the Three-hump camel function 2019-05-26 01:07:12 -05:00
Suryoday Basak 5200538009 Including the Himmelblau function 2019-05-26 00:01:40 -05:00
Suryoday Basak 273b846326 Including Levi function N.13 2019-05-25 01:04:45 -05:00
Suryoday Basak 14225164e3 Including the Goldstein-Price function 2019-05-24 18:26:28 -05:00
Suryoday Basak 51399ea2cc Including the Beale function 2019-05-24 00:04:23 -05:00
Suryoday Basak e13836be9d Fixing style issues in ackley_function 2019-05-23 17:07:51 -05:00
Ryan Curtin 8bea8d214b Update and release version 1.15.1. 2019-05-23 00:00:29 -04:00
Ryan Curtin da2a0362a6 Update history. 2019-05-23 00:00:11 -04:00
Ryan Curtin ff448669f8 Merge pull request #116 from coatless/code-style-warnings
Enable minimal code style checks
2019-05-22 20:58:39 -07:00
Suryoday Basak 8556bc1ad1 Including ackley_function.hpp in problems.hpp 2019-05-22 13:30:04 -05:00
Suryoday Basak d01fd637d3 Including Ackley function 2019-05-22 13:19:12 -05:00
James Balamuta 445eefc282 Merge remote-tracking branch 'upstream/master' into code-style-warnings 2019-05-21 18:07:11 -05:00
Marcus Edel 2be1d7b908 Merge pull request #115 from coatless/fix-warnings
Fix compilation warnings.
2019-05-21 21:22:12 +02:00
James Balamuta 0ca775e870 Set error on warnings only during travis build 2019-05-21 13:06:28 -05:00
James Balamuta df633eee38 Partially align CMAKE code with MLPACK
- Removed the "SHOW_COMPILE_OPTIONS"
- Added chunk to detect compiler and set appropriate values
- Dropped `-Wextra` in favor of specific diagnostics
2019-05-21 13:05:50 -05:00
Suryoday Basak b3a6ef4d38 Committing changes to CMakeLists.txt 2019-05-21 12:25:39 -05:00
James Balamuta 81b7efb0a3 Remove shuffle parameter from SPSA docs 2019-05-21 12:14:45 -05:00
Suryoday Basak f06e282723 Revert "Including description of PSO"
This reverts commit b9ac5c097c.
2019-05-21 11:21:40 -05:00
James Balamuta 67fd378378 Remove showing color diagnostics... 2019-05-20 15:45:19 -05:00
James Balamuta 02903a5d19 Enable minimal code style checks
When building ensmallen on TravisCI, verify the code passes traditional ISO checks given by:

```
-Wall -Wpedantic -Wextra
```

If not, treat any warnings that arise as errors with `-Werror`. Show the errors clearly by colorizing the diagnostic output using `-fdiagnostics-color`.

These checks are generally used by CRAN:

https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Writing-portable-packages
2019-05-20 15:40:41 -05:00
James Balamuta 80d45404bb Add changes in history 2019-05-20 14:24:00 -05:00
James Balamuta f8d5181b65 Remove unused shuffle parameter from spsa
Deletes all instances of `shuffle` from the `spsa` implementation

 ../inst/include/ensmallen_bits/spsa/spsa.hpp:120:8:
warning: private field 'shuffle' is not used [-Wunused-private-field]
  bool shuffle;
2019-05-20 14:20:45 -05:00
James Balamuta a13eb42db5 Fix parameter initialization issue in qhadam_update
Addresses the variable initialization issue highlighted under -Wreorder, c.f.

 ../inst/include/ensmallen_bits/qhadam/qhadam_update.hpp:57:5: warning: field 'v2' will be initialized after field 'iteration' [-Wreorder]
2019-05-20 13:49:56 -05:00
Ryan Curtin 837390f35f Update and release version 1.15.0. 2019-05-14 23:17:45 -04:00
Ryan Curtin 1a0b6282d0 Update QHAdam and QHSGD. 2019-05-14 23:16:57 -04:00
Ryan Curtin 92e2417b3f Merge remote-tracking branch 'origin/master' into templated_optimize 2019-05-14 22:56:32 -04:00
Ryan Curtin 381eff1acd Update deprecated definition to match Armadillo. 2019-05-14 22:56:12 -04:00
Ryan Curtin 70f32d2ace Adapt DE optimizer (must have missed it). 2019-05-14 22:55:49 -04:00
Ryan Curtin 18817602b3 Use parent members directly. 2019-05-14 22:55:19 -04:00
Ryan Curtin d7cb084902 Fix documentation: also document new template parameters. 2019-05-14 21:41:45 -04:00
Ryan Curtin 4fbe544219 Merge branch 'niteya-shah-QHAdam' 2019-05-14 21:27:40 -04:00
Ryan Curtin 5d1d96eb01 Update HISTORY.txt. 2019-05-14 21:27:27 -04:00
Ryan Curtin 7e8108d0c7 Style fixes. 2019-05-14 21:27:21 -04:00
Ryan Curtin 31d9393d1b Merge branch 'QHAdam' of https://github.com/niteya-shah/ensmallen into niteya-shah-QHAdam 2019-05-14 20:41:49 -04:00
Niteya Shah 0f17a19ff4 documentation fixes wrt review 2019-05-13 15:42:14 +05:30
Ryan Curtin 39c965f757 Fix notes from Conrad's review. 2019-05-12 21:56:07 -04:00
Ryan Curtin 3c3bdd4b98 Disable tests that will fail when we have a too-old Armadillo version. 2019-05-12 21:43:25 -04:00
Ryan Curtin 98e2714e96 Adapt AdaptiveStepsize to use a Policy to store MatTypes. 2019-05-11 21:11:37 -04:00
Ryan Curtin 03ac6d9622 Merge remote-tracking branch 'origin/master' into templated_optimize 2019-05-11 20:53:32 -04:00
Ryan Curtin 743ea3a43d Adapt SPSA. 2019-05-11 20:52:14 -04:00
Ryan Curtin 71acbe4388 Merge remote-tracking branch 'origin/master' into templated_optimize 2019-05-11 19:46:23 -04:00
Ryan Curtin cd2f801150 Re-add and adapt FunctionTest. 2019-05-11 19:40:26 -04:00
Ryan Curtin 3c2acbb8c7 Update documentation where needed. 2019-05-11 18:57:43 -04:00
Ryan Curtin 44a51bbafb Update SDP tests. 2019-05-11 18:57:28 -04:00
Ryan Curtin 31b6551618 Add FTML test. 2019-05-11 18:57:16 -04:00
Ryan Curtin 9a4f01c12a Adapt optimizers to use new checks. 2019-05-11 18:57:02 -04:00
Ryan Curtin 9cc1d24a7a Add checks for allowed MatTypes, etc. 2019-05-11 18:56:41 -04:00
Ryan Curtin ae269d81ad Add FTML test. 2019-05-11 18:56:24 -04:00
Ryan Curtin e6b7bbcbca Uncomment FunctionType checks. 2019-05-11 12:28:16 -04:00
Ryan Curtin 6e6becec86 Fix failing compilation and tests. 2019-04-30 00:21:35 -04:00
Ryan Curtin 57b1507d4d Add and update AugLagrangian and LRSDP tests. 2019-04-30 08:21:45 +00:00
Ryan Curtin 3f4ee7a3b4 Refactor nearly all optimizers. 2019-04-30 08:18:52 +00:00
Ryan Curtin 261f71db40 Update BigBatchSGD, Eve, FTML, Padam, and part of IQN.
Needs Armadillo sparse vectorise() support next.
2019-04-20 07:44:47 -04:00
Niteya 14cddeaefa doc fixes 2019-04-15 13:37:03 +05:30
Suryoday Basak b9ac5c097c Including description of PSO 2019-04-13 22:56:35 -05:00
Suryoday Basak 564705dabe Fixing typos in function names 2019-03-29 19:50:18 -05:00
Suryoday Basak 7c6fa9f682 Including updated tests for constrained pso 2019-03-29 19:40:31 -05:00
Suryoday Basak 2fd958e072 Removing spurious includes and variable descriptions 2019-03-29 19:39:10 -05:00
Niteya 6a81c9127b Documentation Fixes 2019-03-28 09:24:38 +05:30
Niteya 438aea65e4 Documentation FIxes and parameterisation change 2019-03-27 17:38:53 +05:30
Niteya 692b692497 documentation fixes 2019-03-27 14:09:05 +05:30
Ryan Curtin fcafc11720 Adapt AdaDelta and AdaGrad and associated test problems.
Also fix memory handling bug.
2019-03-26 22:15:24 -04:00
Ryan Curtin fb076d61fc Add utility Any class. 2019-03-26 20:13:58 -04:00
Niteya 38d913ec02 Changed some test parameters 2019-03-26 23:56:39 +05:30
Niteya a40b2a1857 documentation fix 2019-03-26 23:09:20 +05:30
Niteya 97b9494480 added to function types 2019-03-26 23:04:20 +05:30
Niteya 9ec5eb4f0a Documentation Fixes and Added test for QHSGD 2019-03-26 22:50:40 +05:30
Niteya 32ab0d7f7e Added changes to reflect those of AdamW 2019-03-26 22:31:27 +05:30
Ryan Curtin a1dd5825e4 Adapt SGD to have templated MatType and GradType. Works. 2019-03-25 21:48:18 -04:00
Ryan Curtin fdb42c91d3 Templatize SGDTestFunction. 2019-03-25 21:48:05 -04:00
Ryan Curtin 4e7e65a363 Fix incorrect type. 2019-03-25 21:47:52 -04:00
Ryan Curtin bb2d368552 Actually these checks are not working right yet. 2019-03-25 21:47:33 -04:00
Ryan Curtin 00e1dc3dd7 Fix static checks to work with MatType/GradType. 2019-03-23 01:05:58 -04:00
Ryan Curtin d11940cffb Add MatType and GradType to decomposable functions. 2019-03-21 20:41:47 -04:00
Ryan Curtin a6b7f8c4c8 Make lines fit a little bit better.
Hard to do with such crazy template metaprogramming...
2019-03-20 22:08:32 -04:00
Ryan Curtin 00f1b4012a Re-add different L-BFGS tests. 2019-03-20 22:08:25 -04:00
Ryan Curtin cf13217070 Allow L-BFGS to have different objective and gradient types. 2019-03-20 22:08:06 -04:00
Ryan Curtin a8a00fd921 Adapt some of the problem functions. 2019-03-20 22:07:47 -04:00
niteya-shah 35d1868fbe Merge branch 'master' into QHAdam 2019-03-18 14:35:22 +05:30
Niteya 6bc84bf6a6 fix to resolve conflict 2019-03-18 14:31:51 +05:30
Niteya d8a7274318 Added documentation 2019-03-18 10:24:14 +05:30
Suryoday Basak f251a5f422 Including PSO test for linear constrained affine function 2019-03-12 07:38:05 -06:00
Suryoday Basak a9b202113d Including method for constrained optimization 2019-03-12 07:08:40 -06:00
Suryoday Basak 89acdc6f88 Merge branch 'master' of git://github.com/mlpack/ensmallen into pso-dev
Need to get commits fixing SPSA parameters.
2019-03-05 22:13:55 -06:00
Suryoday Basak 7cf17a7440 Merge branch 'master' of git://github.com/mlpack/ensmallen into pso-dev
Updating my fork of the repository.
2019-02-28 01:58:07 -06:00
Suryoday Basak d8b81a3bed Fixing test case names in pso_test 2019-02-28 01:33:21 -06:00
Niteya 63238ad0a7 Fix to Parameter type
incorrect parameter type of double assigned to variable
2019-02-25 02:44:39 +05:30
niteya-shah e1d991760a Update qhadam_update.hpp 2019-02-24 05:06:27 +05:30
Niteya 0434e7c636 Merge remote-tracking branch 'niteya-shah/QHAdam' into QHAdam 2019-02-24 05:04:14 +05:30
Niteya 30ffe57c51 Style Fixes and additional documentation 2019-02-24 05:00:33 +05:30
Niteya d1976ed8ab Style Fixes and additional documentation 2019-02-24 04:58:05 +05:30
Niteya adb7efee7c Added Tests for QHAdam 2019-02-24 00:59:43 +05:30
Suryoday Basak 604a1de6b7 Including tests for PSO 2019-02-16 18:54:35 -06:00
Suryoday Basak 5bc5b713b6 Adding implementation of PSO from old mlpack version 2019-02-16 07:35:53 -06:00
Suryoday Basak 2457a77976 Adding an implementation of PSO 2019-02-16 07:32:37 -06:00
Niteya 60672c1369 Fixes to Adam and addition of QHSGD
1) Added SGD Update QHSGD
2) fixed some Adam issues wrt QHAdam
2019-02-16 17:21:30 +05:30
Niteya 9f481abb1a Added inline to template Specialisation 2019-02-16 13:17:40 +05:30
Niteya 160c1f3259 Implementation of QHAdam Update
QHAdam updates and an overloaded constructor for QHadam
2019-02-15 23:55:38 +05:30
Niteya 39af38de3e First Commit
Empty File , will discuss with assignee about the PR
2019-02-12 12:15:01 +05:30
Ryan Curtin e7c8515692 Intermediate attempts. 2019-01-14 21:04:19 -05:00
288 changed files with 22924 additions and 5735 deletions
+57
View File
@@ -0,0 +1,57 @@
environment:
ARMADILLO_DOWNLOAD: "http://ftp.fau.de/macports/distfiles/armadillo/armadillo-8.400.0.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
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
VSVER: Visual Studio 16 2019
MSBUILD: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe
configuration: Release
install:
- ps: nuget install OpenBLAS -o "${env:APPVEYOR_BUILD_FOLDER}"
build_script:
# First, download and build Armadillo.
- 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
- >
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_BUILD_TYPE=Release ..
- >
"%MSBUILD%" "armadillo.sln"
/m /verbosity:quiet /p:Configuration=Release;Platform=x64
- cd ../..
# Now build ensmallen.
- cd ensmallen && mkdir build && cd build
- >
cmake -G "%VSVER%"
-DARMADILLO_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER%/../armadillo-8.400.0/include/
-DARMADILLO_LIBRARIES=%BLAS_LIBRARY%
-DLAPACK_LIBRARY=%BLAS_LIBRARY%
-DBLAS_LIBRARY=%BLAS_LIBRARY%
-DCMAKE_BUILD_TYPE=Release ..
- >
"%MSBUILD%" "ensmallen.sln"
/m /verbosity:minimal /nologo /p:BuildInParallel=true
# Run tests after copying libraries.
- ps: cp C:\projects\ensmallen\OpenBLAS.0.2.14.1\lib\native\bin\x64\*.* C:\projects\ensmallen\build\
- ctest -C Release -V --output-on-failure .
+1
View File
@@ -0,0 +1 @@
build/
+20 -14
View File
@@ -1,26 +1,32 @@
sudo: required
os: linux
dist: trusty
language: cpp
env:
- ARMADILLO=latest
- ARMADILLO=latest SANITY_HISTORY=perform
- ARMADILLO=minimum
before_install:
- sudo apt-get update
- sudo apt-get install -y --allow-unauthenticated libopenblas-dev liblapack-dev g++
- 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*;
fi
- if [ $ARMADILLO == "minimum" ]; then
curl https://ftp.fau.de/macports/distfiles/armadillo/armadillo-6.500.5.tar.gz | tar xvz && cd armadillo*;
fi
- cmake . && make && sudo make install && cd ..
stages:
- test
- name: sanity
if: type = pull_request AND env(SANITY_HISTORY) = "perform"
install:
- mkdir build && cd build && cmake .. && make -j2
jobs:
include:
- stage: sanity
name: "HISTORY.md Check"
script: sh ./scripts/history-update-check.sh
script:
- sudo apt-get update
- sudo apt-get install -y --allow-unauthenticated libopenblas-dev liblapack-dev g++ xz-utils
- 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*;
fi
- cmake . && make && sudo make install && cd ..
- mkdir build && cd build && cmake -DCMAKE_CXX_FLAGS="-Werror" -DCMAKE_C_FLAGS="-Werror" .. && make -j2
- CTEST_OUTPUT_ON_FAILURE=1 travis_wait 30 ctest -j2
notifications:
+6
View File
@@ -366,4 +366,10 @@ mark_as_advanced(
ARMADILLO_INCLUDE_DIR
ARMADILLO_LIBRARIES)
if (ARMADILLO_FOUND AND NOT TARGET Armadillo:Armadillo)
add_library(Armadillo::Armadillo INTERFACE IMPORTED)
set_target_properties(Armadillo::Armadillo PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ARMADILLO_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${ARMADILLO_LIBRARIES}")
endif()
#======================
+4
View File
@@ -0,0 +1,4 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake)
check_required_components(ensmallen)
+80 -41
View File
@@ -1,55 +1,92 @@
# 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 2.8.10)
project(ensmallen C CXX)
cmake_minimum_required(VERSION 3.3.2)
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)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake")
# Ensure we have C++11 features. Since we support CMake < 3.1, this needs a
# little bit of special handling.
if ((${CMAKE_MAJOR_VERSION} LESS 3 OR
(${CMAKE_MAJOR_VERSION} EQUAL 3 AND ${CMAKE_MINOR_VERSION} LESS 1))
AND NOT FORCE_CXX11)
# Older versions of CMake do not support target_compile_features(), so we have
# to use something kind of hacky.
include(CMake/CXX11.cmake)
check_for_cxx11_compiler(HAS_CXX11)
if(NOT HAS_CXX11)
message(FATAL_ERROR "No C++11 compiler available!")
endif()
enable_cxx11()
# Set required C++ standard to C++11.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Extract version from sources.
set(ENSMALLEN_VERSION_FILE_NAME "${PROJECT_SOURCE_DIR}/include/ensmallen_bits/ens_version.hpp")
if(NOT EXISTS ${ENSMALLEN_VERSION_FILE_NAME})
message(FATAL_ERROR "Can't read ${ENSMALLEN_VERSION_FILE_NAME}")
endif()
file(READ ${ENSMALLEN_VERSION_FILE_NAME} ENSMALLEN_VERSION_FILE_CONTENTS)
string(REGEX REPLACE ".*#define ENS_VERSION_MAJOR ([0-9]+).*" "\\1" ENSMALLEN_VERSION_MAJOR "${ENSMALLEN_VERSION_FILE_CONTENTS}")
string(REGEX REPLACE ".*#define ENS_VERSION_MINOR ([0-9]+).*" "\\1" ENSMALLEN_VERSION_MINOR "${ENSMALLEN_VERSION_FILE_CONTENTS}")
string(REGEX REPLACE ".*#define ENS_VERSION_PATCH ([0-9]+).*" "\\1" ENSMALLEN_VERSION_PATCH "${ENSMALLEN_VERSION_FILE_CONTENTS}")
message(STATUS "Configuring ensmallen ${ENSMALLEN_VERSION_MAJOR}.${ENSMALLEN_VERSION_MINOR}.${ENSMALLEN_VERSION_PATCH}")
set(VERSION "${ENSMALLEN_VERSION_MAJOR}.${ENSMALLEN_VERSION_MINOR}.${ENSMALLEN_VERSION_PATCH}")
# Create library target.
add_library(ensmallen INTERFACE)
target_include_directories(ensmallen INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
# Set warning flags for target.
if(MSVC)
target_compile_options(ensmallen INTERFACE $<BUILD_INTERFACE:/Wall>)
else()
# set required standard to c++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif ()
target_compile_options(ensmallen INTERFACE $<BUILD_INTERFACE:-Wall -Wpedantic -Wunused-parameter>)
endif()
# Detect OpenMP support in a compiler. If the compiler supports OpenMP, flags
# to compile with OpenMP are returned and added for compilation.
if (USE_OPENMP)
find_package(OpenMP)
endif ()
# Find OpenMP and link it.
if(USE_OPENMP)
if(NOT TARGET OpenMP::OpenMP_CXX)
find_package(Threads REQUIRED)
add_library(OpenMP::OpenMP_CXX IMPORTED INTERFACE)
set_property(TARGET OpenMP::OpenMP_CXX
PROPERTY INTERFACE_COMPILE_OPTIONS ${OpenMP_CXX_FLAGS})
# Only works if the same flag is passed to the linker; use CMake 3.9+ otherwise (Intel, AppleClang).
set_property(TARGET OpenMP::OpenMP_CXX
PROPERTY INTERFACE_LINK_LIBRARIES ${OpenMP_CXX_FLAGS} Threads::Threads)
endif()
target_link_libraries(ensmallen INTERFACE OpenMP::OpenMP_CXX)
endif()
if (OPENMP_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
else ()
# Disable warnings for all the unknown OpenMP pragmas.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas")
endif ()
# Find Armadillo and link it.
find_package(Armadillo 8.400.0 REQUIRED)
target_link_libraries(ensmallen INTERFACE Armadillo::Armadillo)
# The only dependency we need is Armadillo.
#
# We keep the minimum version in sync with mlpack, otherwise we could have
# irritating compatibility issues.
find_package(Armadillo 6.500.0 REQUIRED)
include_directories(BEFORE "${ARMADILLO_INCLUDE_DIR}")
include_directories(BEFORE "${CMAKE_SOURCE_DIR}/include/")
# Set helper variables for creating the version, config and target files.
include(CMakePackageConfigHelpers)
set(ENSMALLEN_CMAKE_DIR "lib/cmake/ensmallen" CACHE STRING
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(VERSION_CONFIG "${PROJECT_BINARY_DIR}/ensmallen-config-version.cmake")
set(PROJECT_CONFIG "${PROJECT_BINARY_DIR}/ensmallen-config.cmake")
set(TARGETS_EXPORT_NAME ensmallen-targets)
# Install the headers to the correct location.
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(${VERSION_CONFIG}
VERSION ${VERSION}
COMPATIBILITY AnyNewerVersion)
configure_package_config_file(${PROJECT_SOURCE_DIR}/CMake/ensmallen-config.cmake.in
${PROJECT_CONFIG}
INSTALL_DESTINATION ${ENSMALLEN_CMAKE_DIR})
export(TARGETS ensmallen NAMESPACE ensmallen::
FILE ${PROJECT_BINARY_DIR}/${TARGETS_EXPORT_NAME}.cmake)
# Install version, config and target files.
install(FILES ${PROJECT_CONFIG} ${VERSION_CONFIG}
DESTINATION ${ENSMALLEN_CMAKE_DIR})
install(EXPORT ${TARGETS_EXPORT_NAME} DESTINATION ${ENSMALLEN_CMAKE_DIR}
NAMESPACE ensmallen::)
# Export the targets and install the header files.
install(TARGETS ensmallen EXPORT ${TARGETS_EXPORT_NAME} DESTINATION lib)
install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/ensmallen_bits"
DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
PATTERN "*~" EXCLUDE
@@ -57,6 +94,8 @@ install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/ensmallen_bits"
install(FILES ${CMAKE_SOURCE_DIR}/include/ensmallen.hpp
DESTINATION "${CMAKE_INSTALL_PREFIX}/include")
# Enable testing and build tests.
enable_testing()
add_subdirectory(tests)
if (BUILD_TESTS)
add_subdirectory(tests)
endif()
+137 -1
View File
@@ -42,4 +42,140 @@ you have written a test like this, make sure it does not fail often by
uncommenting the code that sets a random seed in `tests/main.cpp` and running
your test many times.
Information on how to build and run the tests is in the main README.md file.
### Install Dependencies
<details open>
<summary>Linux</summary>
Use your distributions' package manager to install the required dependencies. For Ubuntu/Debian, the commands are as shown below.
```bash
$ sudo apt-get update
$ sudo apt-get install libarmadillo-dev cmake
```
To install the dependencies on Fedora/RHEL/CentOS, you can use:
```bash
$ yum install armadillo-devel cmake
```
You can install **ensmallen** on Arch from AUR using:
```bash
$ git clone https://aur.archlinux.org/ensmallen.git
$ cd ensmallen
$ makepkg -si
```
</details>
<details open>
<summary>macOS</summary>
You can install **ensmallen** directly with [Homebrew](https://brew.sh).
```bash
$ brew install ensmallen
```
If you want to build **ensmallen** tests from source, use a package manager like [Homebrew](https://brew.sh) to get the necessary dependencies.
```bash
$ brew install armadillo cmake
```
</details>
<details open>
<summary>Windows</summary>
You can install **ensmallen** directly by using [vcpkg](https://github.com/microsoft/vcpkg).
```
vcpkg install ensmallen:x64-windows
```
</details>
### Build and Test
This section describes how to build the **ensmallen** tests from source. **ensmallen** uses CMake as its build system and [Catch2](https://github.com/catchorg/Catch2) as the unit test framework.
First, clone the source code from Github and change into the cloned directory. Or alternatively, you can download the latest relese from the [website](http://ensmallen.org) and extract it.
```bash
$ git clone https://github.com/mlpack/ensmallen
$ cd ensmallen
# - or -
$ wget http://ensmallen.org/files/ensmallen-2.14.2.tar.gz
$ tar -xvzpf ensmallen-2.14.2.tar.gz
$ cd ensmallen-latest
```
Next, make a build directory and change into that directory.
```bash
$ mkdir build
$ cd build
```
Then, run the cmake command followed by the make command in the build directory. If the cmake command fails, you probably have missing dependencies.
```bash
$ cmake ..
# or with -w flag to inhibit all warning messages
# $ cmake -DCMAKE_CXX_FLAGS="-w" -DCMAKE_C_FLAGS="-w" .. #
$ make
# or alternately make with the -jN flag to run parallel jobs
# $ make -j4
```
Now, you can either run all of the tests or an individual test case with:
```bash
$ ./ensmallen_tests
$ ./ensmallen_tests <test name>
```
You can list all tests with:
```bash
$ ./ensmallen-tests -l
ensmallen version: 2.10.5 (Fried Chicken)
armadillo version: 9.800.3 (Horizon Scraper)
All available test cases:
SimpleAdaDeltaTestFunction
[AdaDeltaTest]
...
262 test cases
```
## Release process
New releases of ensmallen can be performed almost-automatically with the
`rel/ensmallen-release.sh` script. Releases can only be performed by
contributors with push permissions to the repository. Before making a release,
make sure that all the tests are passing and the release number satisfies the
versioning guidelines in `UPDATING.md` and make sure that `HISTORY.md` is
up-to-date with the new release's release notes (and date).
The script should be run, e.g.,
```
$ rel/ensmallen-release.sh 2 10 0 "Fried Chicken"
```
If the version is a new minor version (or major version), choose a name for the
release. Previous release names have generally been entirely arbitrary.
Then, after running the script, a release needs to be done on the Github
website:
https://github.com/mlpack/ensmallen/releases/new
The format for the release notes is just the release date at the top (e.g.,
`Released Sept. 7th, 2019`), followed by the Markdown-formatted HISTORY.md
updates for that release.
+4 -1
View File
@@ -36,7 +36,10 @@ Copyright:
Copyright 2018, Conrad Sanderson
Copyright 2018, Dan Timson
Copyright 2019, Rahul Ganesh Prabhu
Copyright 2019, Roberto Hueso <robertohueso96@gmail.com>
Copyright 2019, Sayan Goswami <sayan.goswami.106@gmail.com>
Copyright 2020, Joe Dinius <josephwdinius@gmail.com>
License: BSD-3-clause
All rights reserved.
.
+250 -35
View File
@@ -1,89 +1,304 @@
### ensmallen 1.14.4
### ensmallen 2.14.2: "No Direction Home"
###### 2020-08-31
* Fix implementation of fonesca fleming problem function f1 and f2
type usage and negative signs. ([#223](https://github.com/mlpack/ensmallen/pull/223))
### ensmallen 2.14.1: "No Direction Home"
###### 2020-08-19
* Fix release script (remove hardcoded information, trim leading whitespaces
introduced by `wc -l` in MacOS)
([#216](https://github.com/mlpack/ensmallen/pull/216),
[#220](https://github.com/mlpack/ensmallen/pull/220)).
* Adjust tolerance for AugLagrangian convergence based on element type
([#217](https://github.com/mlpack/ensmallen/pull/217)).
### ensmallen 2.14.0: "No Direction Home"
###### 2020-08-10
* Add NSGA2 optimizer for multi-objective functions
([#149](https://github.com/mlpack/ensmallen/pull/149)).
* Update automatic website update release script
([#207](https://github.com/mlpack/ensmallen/pull/207)).
* Clarify and fix documentation for constrained optimizers
([#201](https://github.com/mlpack/ensmallen/pull/201)).
* Fix L-BFGS convergence when starting from a minimum
([#201](https://github.com/mlpack/ensmallen/pull/201)).
### ensmallen 2.13.0: "Automatically Automated Automation"
###### 2020-07-15
* Fix CMake package export
([#198](https://github.com/mlpack/ensmallen/pull/198)).
* Allow early stop callback to accept a lambda function
([#165](https://github.com/mlpack/ensmallen/pull/165)).
### ensmallen 2.12.1: "Stir Crazy"
###### 2020-04-20
* Fix total number of epochs and time estimation for ProgressBar callback
([#181](https://github.com/mlpack/ensmallen/pull/181)).
* Handle SpSubview_col and SpSubview_row in Armadillo 9.870
([#194](https://github.com/mlpack/ensmallen/pull/194)).
* Minor documentation fixes
([#197](https://github.com/mlpack/ensmallen/pull/197)).
### ensmallen 2.12.0: "Stir Crazy"
###### 2020-03-28
* Correction in the formulation of sigma in CMA-ES
([#183](https://github.com/mlpack/ensmallen/pull/183)).
* Remove deprecated methods from PrimalDualSolver implementation
([#185](https://github.com/mlpack/ensmallen/pull/185).
* Update logo ([#186](https://github.com/mlpack/ensmallen/pull/186)).
### ensmallen 2.11.5: "The Poster Session Is Full"
###### 2020-03-11
* Change "mathematical optimization" term to "numerical optimization" in the
documentation ([#177](https://github.com/mlpack/ensmallen/pull/177)).
### ensmallen 2.11.4: "The Poster Session Is Full"
###### 2020-03-03
* Require new HISTORY.md entry for each PR.
([#171](https://github.com/mlpack/ensmallen/pull/171),
[#172](https://github.com/mlpack/ensmallen/pull/172),
[#175](https://github.com/mlpack/ensmallen/pull/175)).
* Update/fix example documentation
([#174](https://github.com/mlpack/ensmallen/pull/174)).
### ensmallen 2.11.3: "The Poster Session Is Full"
###### 2020-02-19
* Prevent spurious compiler warnings
([#161](https://github.com/mlpack/ensmallen/pull/161)).
* Fix minor memory leaks
([#167](https://github.com/mlpack/ensmallen/pull/167)).
* Revamp CMake configuration
([#152](https://github.com/mlpack/ensmallen/pull/152)).
### ensmallen 2.11.2: "The Poster Session Is Full"
###### 2020-01-16
* Allow callback instantiation for SGD based optimizer
([#138](https://github.com/mlpack/ensmallen/pull/155)).
* Minor test stability fixes on i386
([#156](https://github.com/mlpack/ensmallen/pull/156)).
* Fix Lookahead MaxIterations() check.
([#159](https://github.com/mlpack/ensmallen/pull/159)).
### ensmallen 2.11.1: "The Poster Session Is Full"
###### 2019-12-28
* Fix Lookahead Synchronization period type
([#153](https://github.com/mlpack/ensmallen/pull/153)).
### ensmallen 2.11.0: "The Poster Session Is Full"
###### 2019-12-24
* Add Lookahead
([#138](https://github.com/mlpack/ensmallen/pull/138)).
* Add AdaBound and AMSBound
([#137](https://github.com/mlpack/ensmallen/pull/137)).
### ensmallen 2.10.5: "Fried Chicken"
###### 2019-12-13
* SGD callback test 32-bit safety (big number)
([#143](https://github.com/mlpack/ensmallen/pull/143)).
* Use "arbitrary" and "separable" terms in static function type checks
([#145](https://github.com/mlpack/ensmallen/pull/145)).
* Remove 'using namespace std' from `problems/` files
([#147](https://github.com/mlpack/ensmallen/pull/147)).
### ensmallen 2.10.4: "Fried Chicken"
###### 2019-11-18
* Add optional tests building.
([#141](https://github.com/mlpack/ensmallen/pull/141)).
* Make code samples collapsible in the documentation.
([#140](https://github.com/mlpack/ensmallen/pull/140)).
### ensmallen 2.10.3: "Fried Chicken"
###### 2019-09-26
* Fix ParallelSGD runtime bug.
([#135](https://github.com/mlpack/ensmallen/pull/135)).
* Add additional L-BFGS convergence check
([#136](https://github.com/mlpack/ensmallen/pull/136)).
### ensmallen 2.10.2: "Fried Chicken"
###### 2019-09-11
* Add release script to rel/ for maintainers
([#128](https://github.com/mlpack/ensmallen/pull/128)).
* Fix Armadillo version check
([#133](https://github.com/mlpack/ensmallen/pull/133)).
### ensmallen 2.10.1: "Fried Chicken"
###### 2019-09-10
* Documentation fix for callbacks
([#129](https://github.com/mlpack/ensmallen/pull/129).
* Compatibility fixes for ensmallen 1.x
([#131](https://github.com/mlpack/ensmallen/pull/131)).
### ensmallen 2.10.0: "Fried Chicken"
###### 2019-09-07
* All `Optimize()` functions now take any matrix type; so, e.g., `arma::fmat`
or `arma::sp_mat` can be used for optimization. See the documentation for
more details ([#113](https://github.com/mlpack/ensmallen/pull/113),
[#119](https://github.com/mlpack/ensmallen/pull/119)).
* Introduce callback support. Callbacks can be appended as the last arguments
of an `Optimize()` call, and can perform custom behavior at different points
during the optimization. See the documentation for more details
([#119](https://github.com/mlpack/ensmallen/pull/119)).
* Slight speedups for `FrankWolfe` optimizer
([#127](https://github.com/mlpack/ensmallen/pull/127)).
### ensmallen 1.16.2: "Loud Alarm Clock"
###### 2019-08-12
* Fix PSO return type bug
([#126](https://github.com/mlpack/ensmallen/pull/126)).
### ensmallen 1.16.1: "Loud Alarm Clock"
###### 2019-08-11
* Update HISTORY.md to use Markdown links to the PR and add release names.
* Fix PSO return type bug
([#124](https://github.com/mlpack/ensmallen/pull/124)).
### ensmallen 1.16.0: "Loud Alarm Clock"
###### 2019-08-09
* Add option to avoid computing exact objective at the end of the optimization
([#109](https://github.com/mlpack/ensmallen/pull/109)).
* Fix handling of curvature for BigBatchSGD
([#118](https://github.com/mlpack/ensmallen/pull/118)).
* Reduce runtime of tests
([#118](https://github.com/mlpack/ensmallen/pull/118)).
* Introduce local-best particle swarm optimization, `LBestPSO`, for
unconstrained optimization problems
([#86](https://github.com/mlpack/ensmallen/pull/86)).
### ensmallen 1.15.1: "Wrong Side Of The Road"
###### 2019-05-22
* Fix `-Wreorder` in `qhadam` warning
([#115](https://github.com/mlpack/ensmallen/pull/115)).
* Fix `-Wunused-private-field` warning in `spsa`
([#115](https://github.com/mlpack/ensmallen/pull/115)).
* Add more warning output for gcc/clang
([#116](https://github.com/mlpack/ensmallen/pull/116)).
### ensmallen 1.15.0: "Wrong Side Of The Road"
###### 2019-05-14
* Added QHAdam and QHSGD optimizers
([#81](https://github.com/mlpack/ensmallen/pull/81)).
### ensmallen 1.14.4: "Difficult Crimp"
###### 2019-05-12
* Fixes for BigBatchSGD (#91).
* Fixes for BigBatchSGD ([#91](https://github.com/mlpack/ensmallen/pull/91)).
### ensmallen 1.14.3
### ensmallen 1.14.3: "Difficult Crimp"
###### 2019-05-06
* Handle eig_sym() failures correctly (#100).
* Handle `eig_sym()` failures correctly
([#100](https://github.com/mlpack/ensmallen/pull/100)).
### ensmallen 1.14.2
### ensmallen 1.14.2: "Difficult Crimp"
###### 2019-03-14
* SPSA test tolerance fix (#97).
* SPSA test tolerance fix
([#97](https://github.com/mlpack/ensmallen/pull/97)).
* Minor documentation fixes (#95, #98).
* Fix newlines at end of file (#92).
* Fix newlines at end of file
([#92](https://github.com/mlpack/ensmallen/pull/92)).
### ensmallen 1.14.1
### ensmallen 1.14.1: "Difficult Crimp"
###### 2019-03-09
* Fixes for SPSA (#87).
* Fixes for SPSA ([#87](https://github.com/mlpack/ensmallen/pull/87)).
* Optimized CNE and DE (#90). Changed initial population generation in CNE
to be a normal distribution about the given starting point, which should
accelerate convergence.
* Optimized CNE and DE ([#90](https://github.com/mlpack/ensmallen/pull/90)).
Changed initial population generation in CNE to be a normal distribution
about the given starting point, which should accelerate convergence.
### ensmallen 1.14.0
### ensmallen 1.14.0: "Difficult Crimp"
###### 2019-02-20
* Add DE optimizer (#77).
* Add DE optimizer ([#77](https://github.com/mlpack/ensmallen/pull/77)).
* Fix for Cholesky decomposition in CMAES (#83).
* Fix for Cholesky decomposition in CMAES
([#83](https://github.com/mlpack/ensmallen/pull/83)).
### ensmallen 1.13.2
### ensmallen 1.13.2: "Coronavirus Invasion"
###### 2019-02-18
* Minor documentation fixes (#82).
* Minor documentation fixes ([#82](https://github.com/mlpack/ensmallen/pull/82)).
### ensmallen 1.13.1
### ensmallen 1.13.1: "Coronavirus Invasion"
###### 2019-01-24
* Fix -Wreorder warning (#75).
* Fix -Wreorder warning ([#75](https://github.com/mlpack/ensmallen/pull/75)).
### ensmallen 1.13.0
### ensmallen 1.13.0: "Coronavirus Invasion"
###### 2019-01-14
* Enhance options for AugLagrangian optimizer (#66).
* Enhance options for AugLagrangian optimizer
([#66](https://github.com/mlpack/ensmallen/pull/66)).
* Add SPSA optimizer (#69).
* Add SPSA optimizer ([#69](https://github.com/mlpack/ensmallen/pull/69)).
### ensmallen 1.12.2
### ensmallen 1.12.2: "New Year's Party"
###### 2019-01-05
* Fix list of contributors.
### ensmallen 1.12.1
### ensmallen 1.12.1: "New Year's Party"
###### 2019-01-03
* Make sure all files end with newlines.
### ensmallen 1.12.0
### ensmallen 1.12.0: "New Year's Party"
###### 2018-12-30
* Add link to ensmallen PDF to README.md.
* Minor documentation fixes. Remove too-verbose documentation from source for
each optimizer (#61).
each optimizer ([#61](https://github.com/mlpack/ensmallen/pull/61)).
* Add FTML optimizer (#48).
* Add FTML optimizer ([#48](https://github.com/mlpack/ensmallen/pull/48)).
* Add SWATS optimizer (#42).
* Add SWATS optimizer ([#42](https://github.com/mlpack/ensmallen/pull/42)).
* Add Padam optimizer (#46).
* Add Padam optimizer ([#46](https://github.com/mlpack/ensmallen/pull/46)).
* Add Eve optimizer (#45).
* Add Eve optimizer ([#45](https://github.com/mlpack/ensmallen/pull/45)).
* Add ResetPolicy() to SGD-like optimizers (#60).
* Add ResetPolicy() to SGD-like optimizers
([#60](https://github.com/mlpack/ensmallen/pull/60)).
### ensmallen 1.11.1
### ensmallen 1.11.1: "Jet Lag"
###### 2018-11-29
* Minor documentation fixes.
### ensmallen 1.11.0
### ensmallen 1.11.0: "Jet Lag"
###### 2018-11-28
* Add WNGrad optimizer.
* Fix header name in documentation samples.
### ensmallen 1.10.1
### ensmallen 1.10.1: "Corporate Catabolism"
###### 2018-11-16
* Fixes for GridSearch optimizer.
* Include documentation with release.
### ensmallen 1.10.0
### ensmallen 1.10.0: "Corporate Catabolism"
###### 2018-10-20
* Initial release.
+43 -10
View File
@@ -1,10 +1,14 @@
**ensmallen** is a C++ header-only library for mathematical optimization.
<h2 align="center">
<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.
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 mathematical optimization task.
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.
@@ -16,14 +20,21 @@ gradient-free optimizers, and constrained optimization.
* 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
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.
### License
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
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 .
### Citation
@@ -31,10 +42,30 @@ 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).
* 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.
```
@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}
}
```
### Developers and Contributors
@@ -63,3 +94,5 @@ the library.
* Conrad Sanderson
* Dan Timson
* N Rajiv Vaidyanathan
* Roberto Hueso
* Sayan Goswami
+576
View File
@@ -0,0 +1,576 @@
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:
* Changing the learning rate.
* Printing of 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:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
RosenbrockFunction f;
arma::mat coordinates = f.GetInitialPoint();
MomentumSGD optimizer(0.01, 32, 100000, 1e-5, true, MomentumUpdate(0.5));
// Pass the built-in *PrintLoss* callback as the last argument to the
// *Optimize()* function.
optimizer.Optimize(f, coordinates, PrintLoss());
```
</details>
Passing multiple callbacks is just the same as passing a single callback:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
RosenbrockFunction f;
arma::mat coordinates = f.GetInitialPoint();
MomentumSGD optimizer(0.01, 32, 100000, 1e-5, true, MomentumUpdate(0.5));
// Pass the built-in *PrintLoss* and *EarlyStopAtMinLoss* callback as the last
// argument to the *Optimize()* function.
optimizer.Optimize(f, coordinates, PrintLoss(), EarlyStopAtMinLoss());
```
</details>
It is also possible to pass a callback instantiation that allows accessing of
internal callback parameters at a later state:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
RosenbrockFunction f;
arma::mat coordinates = f.GetInitialPoint();
MomentumSGD optimizer(0.01, 32, 100000, 1e-5, true, MomentumUpdate(0.5));
// Create an instantiation of the built-in *StoreBestCoordinates* callback,
// which will store the best objective and the corresponding model parameter
// that can be accessed later.
StoreBestCoordinates<> callback;
// Pass an instantiation of the built-in *StoreBestCoordinates* callback as the
// last argument to the *Optimize()* function.
optimizer.Optimize(f, coordinates, callback);
// Print the minimum objective that is stored inside the *StoreBestCoordinates*
// callback that was passed to the *Optimize()* call.
std::cout << callback.BestObjective() << std::endl;
```
</details>
## Built-in Callbacks
### EarlyStopAtMinLoss
Stops the optimization process if the loss stops decreasing or no improvement
has been made.
#### Constructors
* `EarlyStopAtMinLoss()`
* `EarlyStopAtMinLoss(`_`patience`_`)`
* `EarlyStopAtMinLoss(`_`func`_`)`
* `EarlyStopAtMinLoss(`_`func`_`,`_`patience`_`)`
#### Attributes
| **type** | **name** | **description** | **default** |
|----------|----------|-----------------|-------------|
| `size_t` | **`patience`** | The number of epochs to wait after the minimum loss has been reached. | `10` |
| `std::function<double(const arma::mat&)>` | **`func`** | A callback to return immediate loss evaluated by the function. | |
Note that for the `func` argument above, if a
[different matrix type](#alternate-matrix-types) is desired, instead of using
the class `EarlyStopAtMinLoss`, the class `EarlyStopAtMinLossType<MatType>`
should be used.
#### 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, EarlyStopAtMinLoss());
```
Another example of using lambda in the constructor.
```c++
// Generate random training data and labels.
arma::mat trainingData(5, 100, arma::fill::randu);
arma::Row<size_t> trainingLabels =
arma::randi<arma::Row<size_t>>(100, arma::distr_param(0, 1));
// Generate a validation set.
arma::mat validationData(5, 100, arma::fill::randu);
arma::Row<size_t> validationLabels =
arma::randi<arma::Row<size_t>>(100, arma::distr_param(0, 1));
// Create a LogisticRegressionFunction for both the training and validation data.
LogisticRegressionFunction lrfTrain(trainingData, trainingLabels);
LogisticRegressionFunction lrfValidation(validationData, validationLabels);
// Create a callback that will terminate when the validation loss starts to
// increase.
EarlyStopAtMinLoss cb(
[&](const arma::mat& coordinates)
{
// 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);
```
</details>
### PrintLoss
Callback that prints loss to stdout or a specified output stream.
#### Constructors
* `PrintLoss()`
* `PrintLoss(`_`output`_`)`
#### Attributes
| **type** | **name** | **description** | **default** |
|----------|----------|-----------------|-------------|
| `std::ostream` | **`output`** | Ostream which receives output from this object. | `stdout` |
#### 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, PrintLoss());
```
</details>
### ProgressBar
Callback that prints a progress bar to stdout or a specified output stream.
#### Constructors
* `ProgressBar()`
* `ProgressBar(`_`width`_`)`
* `ProgressBar(`_`width, output`_`)`
#### Attributes
| **type** | **name** | **description** | **default** |
|----------|----------|-----------------|-------------|
| `size_t` | **`width`** | Width of the bar. | `70` |
| `std::ostream` | **`output`** | Ostream which receives output from this object. | `stdout` |
#### 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, ProgressBar());
```
</details>
### StoreBestCoordinates
Callback that stores the model parameter after every epoch if the objective
decreased.
#### Constructors
* `StoreBestCoordinates<`_`ModelMatType`_`>()`
The _`ModelMatType`_ template parameter refers to the matrix type of the model
parameter.
#### Attributes
The stored model parameter can be accessed via the member method
`BestCoordinates()` and the best objective via `BestObjective()`.
#### 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();
StoreBestCoordinates<arma::mat> cb;
optimizer.Optimize(f, coordinates, cb);
std::cout << "The optimized model found by AdaDelta has the "
<< "parameters " << cb.BestCoordinatest();
```
</details>
## Callback States
Callbacks are called at several states during the optimization process:
* 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.
Each callback provides optimization relevant information that can be accessed or
modified.
### BeginOptimization
Called at the beginning of the optimization process.
* `BeginOptimization(`_`optimizer, function, 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. |
### EndOptimization
Called at the end of the optimization process.
* `EndOptimization(`_`optimizer, function, 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. |
### Evaluate
Called after any call to `Evaluate()`.
* `Evaluate(`_`optimizer, function, coordinates, objective`_`)`
#### 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. |
| `double` | **`objective`** | Objective value of the current point. |
### EvaluateConstraint
Called after any call to `EvaluateConstraint()`.
* `EvaluateConstraint(`_`optimizer, function, coordinates, constraint, constraintValue`_`)`
#### 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. |
| `size_t` | **`constraint`** | The index of the constraint. |
| `double` | **`constraintValue`** | Constraint value of the current point. |
### Gradient
Called after any call to `Gradient()`.
* `Gradient(`_`optimizer, function, coordinates, gradient`_`)`
#### 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. |
| `GradType` | **`gradient`** | Matrix that holds the gradient. |
### GradientConstraint
Called after any call to `GradientConstraint()`.
* `GradientConstraint(`_`optimizer, function, coordinates, constraint, gradient`_`)`
#### 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. |
| `size_t` | **`constraint`** | The index of the constraint. |
| `GradType` | **`gradient`** | Matrix that holds the gradient. |
### BeginEpoch
Called at the beginning of a pass over the data. The objective may be exact or
an estimate depending on `exactObjective` value.
* `BeginEpoch(`_`optimizer, function, coordinates, epoch, objective`_`)`
#### 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. |
| `size_t` | **`epoch`** | The index of the current epoch. |
| `double` | **`objective`** | Objective value of the current point. |
### EndEpoch
Called at the end of a pass over the data. The objective may be exact or
an estimate depending on `exactObjective` value.
* `EndEpoch(`_`optimizer, function, coordinates, epoch, objective`_`)`
#### 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. |
| `size_t` | **`epoch`** | The index of the current epoch. |
| `double` | **`objective`** | Objective value of the current point. |
## Custom Callbacks
### Learning rate scheduling
Setting the learning rate is crucially important when training because it
controls both the speed of convergence and the ultimate performance of the
model. One of the simplest learning rate strategies is to have a fixed learning
rate throughout the training process. Choosing a small learning rate allows the
optimizer to find good solutions, but this comes at the expense of limiting the
initial speed of convergence. To overcome this tradeoff, changing the learning
rate as more epochs have passed is commonly done in model training. The
`Evaluate` method in combination with the ``StepSize`` method of the optimizer
can be used to update the variables.
Example code showing how to implement a custom callback to change the learning
rate is given below.
<details>
<summary>Click to collapse/expand example code.
</summary>
```c++
class ExponentialDecay
{
// Set up the exponential decay learning rate scheduler with the user
// specified decay value.
ExponentialDecay(const double decay) : decay(decay), learningRate(0) { }
// Callback function called at the start of the optimization process.
// In this example we will use this to save the initial learning rate.
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginOptimization(OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{
// Save the initial learning rate.
learningRate = optimizer.StepSize();
}
// 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,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t epoch,
const double objective)
{
// Update the learning rate.
optimizer.StepSize() = learningRate * (1.0 - std::pow(decay,
(double) epoch));
}
double learningRate;
};
int main()
{
// First, generate some random data, with 10000 points and 10 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(10, 10000, arma::fill::randn);
arma::rowvec responses(10000, arma::fill::randn);
// Create a starting point for our optimization randomly. The model has 10
// parameters, so the shape is 10x1.
arma::mat startingPoint(10, 1, arma::fill::randn);
// Construct the objective function.
LinearRegressionFunction lrf(data, responses);
arma::mat lrfParams(startingPoint);
// Create the StandardSGD optimizer with specified parameters.
// The ens::StandardSGD type can be replaced with any ensmallen optimizer
//that can handle differentiable functions.
StandardSGD optimizer(0.001, 1, 0, 1e-15, true);
// Use the StandardSGD optimizer with specified parameters to minimize the
// LinearRegressionFunction and pass the *exponential decay*
// callback function from above.
optimizer.Optimize(lrf, lrfParams, ExponentialDecay(0.01));
// Print the trained model parameter.
std::cout << lrfParams.t();
}
```
</details>
### Early stopping at minimum loss
Early stopping is a technique for controlling overfitting in machine learning
models, especially neural networks, by stopping the optimization process before
the model has trained for the maximum number of iterations.
Example code showing how to implement a custom callback to stop the optimization
when the minimum of loss has been reached is given below.
<details>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
// This class implements early stopping at minimum loss callback function to
// terminate the optimization process early if the loss stops decreasing.
class EarlyStop
{
public:
// Set up the early stop at min loss class, which keeps track of the minimum
// loss.
EarlyStop() : bestObjective(std::numeric_limits<double>::max()) { }
// Callback function called at the end of a pass over the data, which provides
// 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 */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double objective)
{
// Check if the given objective is lower as the previous objective.
if (objective < bestObjective)
{
// Update the local objective.
bestObjective = objective;
}
else
{
// Stop the optimization process.
return true;
}
// Do not stop the optimization process.
return false;
}
// Locally-stored best objective.
double bestObjective;
};
int main()
{
// First, generate some random data, with 10000 points and 10 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(10, 10000, arma::fill::randn);
arma::rowvec responses(10000, arma::fill::randn);
// Create a starting point for our optimization randomly. The model has 10
// parameters, so the shape is 10x1.
arma::mat startingPoint(10, 1, arma::fill::randn);
// Construct the objective function.
LinearRegressionFunction lrf(data, responses);
arma::mat lrfParams(startingPoint);
// Create the L_BFGS optimizer with default parameters.
// The ens::L_BFGS type can be replaced with any ensmallen optimizer that can
// handle differentiable functions.
ens::L_BFGS lbfgs;
// Use the L_BFGS optimizer with default parameters to minimize the
// LinearRegressionFunction and pass the *early stopping at minimum loss*
// callback function from above.
lbfgs.Optimize(lrf, lrfParams, EarlyStop());
// Print the trained model parameter.
std::cout << lrfParams.t();
}
```
</details>
Note that we have simply passed an instantiation of `EarlyStop` the
rest is handled inside the optimizer.
ensmallen provides a more complete and general implementation of a
[early stopping](#EarlyStopAtMinLoss) at minimum loss callback function.
+267 -24
View File
@@ -4,6 +4,10 @@ The least restrictive type of function that can be implemented in ensmallen is
a function for which only the objective can be evaluated. For this, a class
with the following API must be implemented:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
class ArbitraryFunctionType
{
@@ -13,6 +17,8 @@ class ArbitraryFunctionType
};
```
</details>
For this type of function, we assume that the gradient `f'(x)` is not
computable. If it is, see [differentiable functions](#differentiable-functions).
@@ -24,6 +30,7 @@ The following optimizers can be used to optimize an arbitrary function:
- [Simulated Annealing](#simulated-annealing-sa)
- [CNE](#cne)
- [DE](#de)
- [PSO](#pso)
- [SPSA](#simultaneous-perturbation-stochastic-approximation-spsa)
Each of these optimizers has an `Optimize()` function that is called as
@@ -32,11 +39,15 @@ Each of these optimizers has an `Optimize()` function that is called as
`Optimize()` is called, `x` will hold the final result of the optimization
(that is, the best `x` found that minimizes `f(x)`).
#### Example: Linear Regression
#### Example: squared function optimization
An example program that implements the objective function f(x) = 2 |x|^2 is
shown below, using the simulated annealing optimizer.
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
@@ -52,7 +63,7 @@ class SquaredFunction
int main()
{
// The minimum is at x = [0 0 0]. Our initial point is chosen to be
// The minimum is at x = [0 0 0]. Our initial point is chosen to be
// [1.0, -1.0, 1.0].
arma::mat x("1.0 -1.0 1.0");
@@ -68,6 +79,8 @@ int main()
}
```
</details>
## Differentiable functions
Probably the most common type of function that can be optimized with ensmallen
@@ -75,6 +88,10 @@ is a differentiable function, where both f(x) and f'(x) can be calculated. To
optimize a differentiable function with ensmallen, a class must be implemented
that follows the API below:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
class DifferentiableFunctionType
{
@@ -99,6 +116,8 @@ class DifferentiableFunctionType
};
```
</details>
Note that you may implement *either* `Evaluate()` and `Gradient()` *or*
`EvaluateWithGradient()`, but it is not mandatory to implement both. (Of
course, supplying both is okay too.) It often results in faster code when
@@ -136,6 +155,10 @@ In the example program, we optimize this objective function and compare the
runtime of an implementation that uses `Evaluate()` and `Gradient()`, and the
runtime of an implementation that uses `EvaluateWithGradient()`.
<details>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
@@ -146,7 +169,7 @@ class LinearRegressionFunction
public:
// Construct the object with the given data matrix and responses.
LinearRegressionFunction(const arma::mat& dataIn,
const arma::rowvec& responsesIn) :
const arma::rowvec& responsesIn) :
data(dataIn), responses(responsesIn) { }
// Return the objective function for model parameters x.
@@ -173,13 +196,13 @@ class LinearRegressionEWGFunction
{
public:
// Construct the object with the given data matrix and responses.
LinearRegressionEWGFunction(const arma::mat& dataIn,
const arma::rowvec& responsesIn) :
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
// Gradient() individually because it caches the computation of
// (responses - x.t() * data)!
double EvaluateWithGradient(const arma::mat& x, arma::mat& g)
{
@@ -248,6 +271,8 @@ int main()
}
```
</details>
### Partially differentiable functions
Some differentiable functions have the additional property that the gradient
@@ -261,6 +286,10 @@ useful for coordinate descent type algorithms.
To use ensmallen optimizers to minimize these types of functions, only two
functions needs to be added to the differentiable function type:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
// Compute the partial gradient f'_j(x) with respect to data coordinate j and
// store it in the sparse matrix g.
@@ -270,6 +299,8 @@ void Gradient(const arma::mat& x, const size_t j, arma::sp_mat& g);
size_t NumFeatures();
```
</details>
**Note**: many partially differentiable function optimizers do not require a
regular implementation of the `Gradient()`, so that function may be omitted.
@@ -295,6 +326,10 @@ function taken across many data points. Implementing an arbitrary separable
function type in ensmallen is similar to implementing an arbitrary objective
function, but with a few extra utility methods:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
class ArbitrarySeparableFunctionType
{
@@ -316,6 +351,8 @@ class ArbitrarySeparableFunctionType
};
```
</details>
Each of the implemented methods is allowed to have additional cv-modifiers
(`static`, `const`, etc.).
@@ -349,6 +386,10 @@ $$ f_i(x) = (\operatorname{responses}(i) - x' * \operatorname{data}(i))^2 $$
where $\operatorname{data}(i)$ represents the data point indexed by $i$ and
$\operatorname{responses}(i)$ represents the observed response indexed by $i$.
<details>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
@@ -425,6 +466,8 @@ int main()
}
```
</details>
## Differentiable separable functions
Likely the most important type of function to be optimized in machine learning
@@ -447,6 +490,10 @@ function taken across many data points. Implementing a differentiable
separable function type in ensmallen is similar to implementing an ordinary
differentiable function, but with a few extra utility methods:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
class ArbitrarySeparableFunctionType
{
@@ -483,7 +530,7 @@ class ArbitrarySeparableFunctionType
//
// Given parameters x and a matrix g, return the sum of the individual
// functions f_i(x) + ... + f_{i + batchSize - 1}(x), and store the sum of
// the gradient of individual functions f'_i(x) + ... +
// the gradient of individual functions f'_i(x) + ... +
// f'_{i + batchSize - 1}(x) into the provided matrix g. g should have the
// same size (rows, columns) as x. i will always be greater than 0, and i +
// batchSize will be less than or equal to the value of NumFunctions().
@@ -494,6 +541,8 @@ class ArbitrarySeparableFunctionType
};
```
</details>
Note that you may implement *either* `Evaluate()` and `Gradient()` *or*
`EvaluateWithGradient()`, but it is not mandatory to implement both. (Of
course, supplying both is okay too.) It often results in faster code when
@@ -503,29 +552,38 @@ and f'(x) compute some of the same intermediate quantities.
Each of the implemented methods is allowed to have additional cv-modifiers
(`static`, `const`, etc.).
The following optimizers can be used with differentiable functions:
The following optimizers can be used with differentiable separable functions:
- [AdaBound](#adabound)
- [AdaDelta](#adadelta)
- [AdaGrad](#adagrad)
- [Adam](#adam)
- [AdaMax](#adamax)
- [AMSBound](#amsbound)
- [AMSGrad](#amsgrad)
- [Big Batch SGD](#big-batch-sgd)
- [Eve](#eve)
- [FTML](#ftml-follow-the-moving-leader)
- [IQN](#iqn)
- [Katyusha](#katyusha)
- [Lookahead](#lookahead)
- [Momentum SGD](#momentum-sgd)
- [Nadam](#nadam)
- [NadaMax](#nadamax)
- [NesterovMomentumSGD](#nesterov-momentum-sgd)
- [OptimisticAdam](#optimisticadam)
- [QHAdam](#qhadam)
- [QHSGD](#qhsgd)
- [RMSProp](#rmsprop)
- [SARAH/SARAH+](#stochastic-recursive-gradient-algorithm-sarahsarah)
- [SGD](#standard-sgd)
- [Stochastic Gradient Descent with Restarts (SGDR)](#stochastic-gradient-descent-with-restarts-sgdr)
- [Snapshot SGDR](#snapshot-stochastic-gradient-descent-with-restarts)
- [SMORMS3](#smorms3)
- [SVRG](#standard-stochastic-variance-reduced-gradient-svrg)
- [SPALeRA](#spalera-stochastic-gradient-descent-spalerasgd)
- [SWATS](#swats)
- [SVRG](#standard-stochastic-variance-reduced-gradient-svrg)
- [WNGrad](#wngrad)
The example program below demonstrates the implementation and use of an
arbitrary separable function. The function used is the linear regression
@@ -542,6 +600,10 @@ represents the observed response indexed by `i`. This example implementation
only implements `EvaluateWithGradient()` in order to avoid redundant
calculations.
<details>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
@@ -625,12 +687,18 @@ int main()
}
```
</details>
### Sparse differentiable separable functions
Some differentiable separable functions have the additional property that
the gradient `f'_i(x)` is sparse. When this is true, one additional method can
be implemented as part of the class to be optimized:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
// Add this definition to use sparse differentiable separable function
// optimizers. Given x, store the sum of the sparse gradient f'_i(x) + ... +
@@ -641,9 +709,15 @@ void Gradient(const arma::mat& x,
const size_t batchSize);
```
</details>
It's also possible to instead use templates to provide only one `Gradient()`
function for both sparse and non-sparse optimizers:
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
// This provides Gradient() for both sparse and non-sparse optimizers.
template<typename GradType>
@@ -653,6 +727,8 @@ void Gradient(const arma::mat& x,
const size_t batchSize);
```
</details>
If either of these methods are available, then any ensmallen optimizer that
optimizes sparse separable differentiable functions may be used. This
includes:
@@ -670,6 +746,10 @@ an `ArbitraryFunctionType`---but for any categorical dimension `x_i` in `x`, the
value will be in the range [0, c_i - 1] where `c_i` is the number of categories
in dimension `x_i`.
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
class CategoricalFunction
{
@@ -679,6 +759,8 @@ class CategoricalFunction
};
```
</details>
However, when an optimizer's Optimize() method is called, two additional
parameters must be specified, in addition to the function to optimize and the
matrix holding the parameters:
@@ -698,6 +780,10 @@ The following optimizers can be used in this way to optimize a categorical funct
An example program showing usage of categorical optimization is shown below.
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
@@ -753,27 +839,82 @@ int main()
}
```
</details>
## Multi-objective functions
A multi-objective optimizer does not return just one set of coordinates at the
minimum of all objective functions, but instead finds a *front* or *frontier* of
possible coordinates that are Pareto-optimal (that is, no individual objective
function's value can be reduced without increasing at least one other
objective function).
In order to optimize a multi-objective function with ensmallen, a `std::tuple<>`
containing multiple `ArbitraryFunctionType`s ([see here](#arbitrary-functions))
should be passed to a multi-objective optimizer's `Optimize()` function.
An example below simultaneously optimizes the generalized Rosenbrock function
in 6 dimensions and the Wood function using [NSGA2](#nsga2).
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
GeneralizedRosenbrockFunction rf(6);
WoodFunction wf;
std::tuple<GeneralizedRosenbrockFunction, WoodFunction> objectives(rf, wf);
// Create an initial point (a random point in 6 dimensions).
arma::mat coordinates(6, 1, arma::fill::randu);
// `coordinates` will be set to the coordinates on the best front that minimize the
// sum of objective functions, and `bestFrontSum` will be the sum of all objectives
// at that coordinate set.
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();
}
```
</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.
The following optimizers can be used with multi-objective functions:
- [NSGA2](#nsga2)
## Constrained functions
A constrained function is an objective function `f(x)` that is also subject to
some constraints on `x`. (For instance, perhaps a constraint could be that `x`
is a positive semidefinite matrix.) ensmallen is able to handle differentiable
objective functions of this type---so, `f'(x)` must also be computable. Given
some set of constraints c_0(x), ... c_M(x), we can re-express our constrained
some set of constraints `c_0(x)`, ..., `c_M(x)`, we can re-express our constrained
objective function as
```
f_C(x) = f(x) + c_0(x) + ... + c_M(x)
```
where the constraint `c_i(x)` is `DBL_MAX` if it is not satisfied, and
otherwise takes some real value. For a "hard constraint", we can simply take
`c_i(x) = 0` when it is satisfied. But allowing `c_i(x)` to return anything
allows us to handle "soft" constraints also.
where the (soft) constraint `c_i(x)` is a positive value if it is not satisfied, and
`0` if it is satisfied. The soft constraint `c_i(x)` should take some value
representing how far from a feasible solution `x` is. It should be
differentiable, since ensmallen's constrained optimizers will use the gradient
of the constraint to find a feasible solution.
In order to optimize a constrained function with ensmallen, a class
implementing the API below is required.
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
class ConstrainedFunctionType
{
@@ -788,21 +929,21 @@ class ConstrainedFunctionType
size_t NumConstraints();
// Evaluate constraint i at the parameters x. If the constraint is
// unsatisfied, DBL_MAX should be returned. If the constraint is satisfied,
// any real value can be returned. The optimizer will add this value to its
// overall objective that it is trying to minimize. (So, a hard constraint
// can just return 0 if it's satisfied.)
// unsatisfied, a value greater than 0 should be returned. If the constraint
// is satisfied, 0 should be returned. The optimizer will add this value to
// its overall objective that it is trying to minimize.
double EvaluateConstraint(const size_t i, const arma::mat& x);
// Evaluate the gradient of constraint i at the parameters x, storing the
// result in the given matrix g. If this is a hard constraint you can set
// the gradient to 0. If the constraint is not satisfied, it could be
// helpful to set the gradient in such a way that the gradient points in the
// result in the given matrix g. If the constraint is not satisfied, the
// gradient should be set in such a way that the gradient points in the
// direction where the constraint would be satisfied.
void GradientConstraint(const size_t i, const arma::mat& x, arma::mat& g);
};
```
</details>
A constrained function can be optimized with the following optimizers:
- [Augmented Lagrangian](#augmented-lagrangian)
@@ -845,10 +986,14 @@ solver. The list of SDP solvers is below:
Example code showing how to solve an SDP is given below.
<details>
<summary>Click to collapse/expand example code.
</summary>
```c++
int main()
{
// We will build a toy semidefinite program and then use the PrimalDualSolver to find a solution
// We will build a toy semidefinite program and then use the PrimalDualSolver to find a solution
// The semi-definite constraint looks like:
//
@@ -938,13 +1083,111 @@ int main()
// use the PrimalDualSolver to solve it.
// ens::PrimalDualSolver could be replaced with ens::LRSDP or other ensmallen
// SDP solvers.
PrimalDualSolver<SDP<arma::sp_mat>> solver(sdp);
PrimalDualSolver solver;
arma::mat X, Z;
arma::vec ysparse, ydense;
// ysparse, ydense, and Z hold the primal and dual variables found during the
// optimization.
const double obj = solver.Optimize(X, ysparse, ydense, Z);
const double obj = solver.Optimize(sdp, X, ysparse, ydense, Z);
std::cout << "SDP optimized with objective " << obj << "." << std::endl;
}
```
</details>
## Alternate matrix types
All of the examples above (and throughout the rest of the documentation)
generally assume that the matrix being optimized has type `arma::mat`. But
ensmallen's optimizers are capable of optimizing more types than just dense
Armadillo matrices. In fact, the full signature of each optimizer's
`Optimize()` method is this:
```
template<typename FunctionType, typename MatType>
typename MatType::elem_type Optimize(FunctionType& function,
MatType& coordinates);
```
The return type, `typename MatType::elem_type`, is just the numeric type held by
the given matrix type. So, for `arma::mat`, the return type is just `double`.
In addition, optimizers for differentiable functions have a third template
parameter, `GradType`, which specifies the type of the gradient. `GradType` can
be manually specified in the situation where, e.g., a sparse gradient is
desired.
It is easy to write a function to optimize, e.g., an `arma::fmat`. Here is an
example, adapted from the `SquaredFunction` example from the
[arbitrary function documentation](#example__squared_function_optimization).
<details open>
<summary>Click to collapse/expand example code.
</summary>
```c++
#include <ensmallen.hpp>
class SquaredFunction
{
public:
// This returns f(x) = 2 |x|^2.
float Evaluate(const arma::fmat& x)
{
return 2 * std::pow(arma::norm(x), 2.0);
}
void Gradient(const arma::fmat& x, arma::fmat& gradient)
{
gradient = 4 * x;
}
};
int main()
{
// The minimum is at x = [0 0 0]. Our initial point is chosen to be
// [1.0, -1.0, 1.0].
arma::fmat x("1.0 -1.0 1.0");
// Create simulated annealing optimizer with default options.
// The ens::SA<> type can be replaced with any suitable ensmallen optimizer
// that is able to handle arbitrary functions.
ens::L_BFGS optimizer;
SquaredFunction f; // Create function to be optimized.
optimizer.Optimize(f, x); // The optimizer will infer arma::fmat!
std::cout << "Minimum of squared function found with simulated annealing is "
<< x;
}
```
</details>
Note that we have simply changed the `SquaredFunction` to accept `arma::fmat`
instead of `arma::mat` as parameters to `Evaluate()`, and the return type has
accordingly been changed to `float` from `double`. It would even be possible to
optimize functions with sparse coordinates by having `Evaluate()` take a sparse
matrix (i.e. `arma::sp_mat`).
If it were desired to represent the gradient as a sparse type, the `Gradient()`
function would need to be modified to take a sparse matrix (i.e. `arma::sp_mat`
or similar), and then you could call `optimizer.Optimize<SquaredFunction,
arma::mat, arma::sp_mat>(f, x);` to perform the optimization while using sparse
matrix types to represent the gradient. Using sparse `MatType` or `GradType`
should *only* be done when it is known that the objective matrix and/or
gradients will be sparse; otherwise the code may run very slow!
ensmallen will automatically infer `MatType` from the call to `Optimize()`, and
check that the given `FunctionType` has all of the necessary functions for the
given `MatType`, throwing a `static_assert` error if not. If you would like to
disable these checks, define the macro `ENS_DISABLE_TYPE_CHECKS` before
including ensmallen:
```
#define ENS_DISABLE_TYPE_CHECKS
#include <ensmallen.hpp>
```
This can be useful for situations where you know that the checks should be
ignored. However, be aware that the code may fail to compile and give more
confusing and difficult error messages!
+825 -79
View File
File diff suppressed because it is too large Load Diff
+30 -10
View File
@@ -29,24 +29,28 @@
#error "please enable C++11/C++14 mode in your compiler"
#endif
#if ((ARMA_VERSION_MAJOR < 6) || ((ARMA_VERSION_MAJOR == 6) && (ARMA_VERSION_MINOR < 500)))
#error "need Armadillo version 6.500 or later"
#if ((ARMA_VERSION_MAJOR < 8) || ((ARMA_VERSION_MAJOR == 8) && (ARMA_VERSION_MINOR < 400)))
#error "need Armadillo version 8.400 or later"
#endif
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <climits>
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <utility>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
// On Visual Studio, disable C4519 (default arguments for function templates)
// since it's by default an error, which doesn't even make any sense because
@@ -59,11 +63,24 @@
#include "ensmallen_bits/ens_version.hpp"
#include "ensmallen_bits/log.hpp" // TODO: should move to another place
#include "ensmallen_bits/utility/any.hpp"
#include "ensmallen_bits/utility/arma_traits.hpp"
// Callbacks.
#include "ensmallen_bits/callbacks/callbacks.hpp"
#include "ensmallen_bits/callbacks/early_stop_at_min_loss.hpp"
#include "ensmallen_bits/callbacks/print_loss.hpp"
#include "ensmallen_bits/callbacks/progress_bar.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_bound/ada_bound.hpp"
#include "ensmallen_bits/ada_delta/ada_delta.hpp"
#include "ensmallen_bits/ada_grad/ada_grad.hpp"
#include "ensmallen_bits/adam/adam.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"
@@ -80,8 +97,11 @@
#include "ensmallen_bits/iqn/iqn.hpp"
#include "ensmallen_bits/katyusha/katyusha.hpp"
#include "ensmallen_bits/lbfgs/lbfgs.hpp"
#include "ensmallen_bits/lookahead/lookahead.hpp"
#include "ensmallen_bits/nsga2/nsga2.hpp"
#include "ensmallen_bits/padam/padam.hpp"
#include "ensmallen_bits/parallel_sgd/parallel_sgd.hpp"
#include "ensmallen_bits/pso/pso.hpp"
#include "ensmallen_bits/rmsprop/rmsprop.hpp"
#include "ensmallen_bits/sa/sa.hpp"
@@ -0,0 +1,209 @@
/**
* @file ada_bound.hpp
* @author Marcus Edel
*
* Class wrapper for the AdaBound and AMSBound 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_ADA_BOUND_HPP
#define ENSMALLEN_ADA_BOUND_HPP
#include <ensmallen_bits/sgd/sgd.hpp>
#include "ada_bound_update.hpp"
#include "ams_bound_update.hpp"
namespace ens {
/**
* AdaBound and AMSBound, employ dynamic bounds on learning rates to achieve a
* gradual and smooth transition from adaptive methods to SGD.
*
* For more information, see the following.
*
* @code
* @inproceedings{Luo2019AdaBound,
* author = {Luo, Liangchen and Xiong, Yuanhao and Liu, Yan and Sun, Xu},
* title = {Adaptive Gradient Methods with Dynamic Bound of Learning
* Rate},
* booktitle = {Proceedings of the 7th International Conference on Learning
* Representations},
* month = {May},
* year = {2019},
* address = {New Orleans, Louisiana}
* }
* @endcode
*
* AdaBound and AMSBound can optimize differentiable separable functions.
* For more details, see the documentation on function types included with this
* distribution or on the ensmallen website.
*/
template<typename UpdatePolicyType = AdaBoundUpdate,
typename DecayPolicyType = NoDecay>
class AdaBoundType
{
public:
/**
* Construct the AdaBoundType optimizer with the given function and
* parameters. AdaBoundType is sensitive to its parameters and hence a good
* hyper paramater 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 finalLr The final (SGD) learning rate.
* @param gamma The convergence speed of the bound functions.
* @param beta1 Exponential decay rate for the first moment estimates.
* @param beta2 Exponential decay rate for the weighted infinity norm
* estimates.
* @param epsilon Value used to initialise the mean 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).
*/
AdaBoundType(const double stepSize = 0.001,
const size_t batchSize = 32,
const double finalLr = 0.1,
const double gamma = 1e-3,
const double beta1 = 0.9,
const double beta2 = 0.999,
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 AdaBoundType. The given starting point
* will be modified to store the finishing point of the algorithm, and the
* final objective value is returned. The DecomposableFunctionType is checked
* for API consistency at compile time.
*
* @tparam DecomposableFunctionType 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 DecomposableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(DecomposableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.template Optimize<DecomposableFunctionType, MatType,
GradType, CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Forward the MatType as GradType.
template<typename DecomposableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(DecomposableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return Optimize<DecomposableFunctionType, MatType, MatType,
CallbackTypes...>(function, iterate,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the final (SGD) learning rate.
double FinalLr() const { return optimizer.UpdatePolicy().FinalLr(); }
//! Modify the final (SGD) learning rate.
double& FinalLr() { return optimizer.UpdatePolicy().FinalLr(); }
//! Get the convergence speed of the bound functions.
double Gamma() const { return optimizer.UpdatePolicy().Gamma(); }
//! Modify the convergence speed of the bound functions.
double& Gamma() { return optimizer.UpdatePolicy().Gamma(); }
//! 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 smoothing parameter.
double Beta1() const { return optimizer.UpdatePolicy().Beta1(); }
//! Modify the smoothing parameter.
double& Beta1() { return optimizer.UpdatePolicy().Beta1(); }
//! Get the second moment coefficient.
double Beta2() const { return optimizer.UpdatePolicy().Beta2(); }
//! Modify the second moment coefficient.
double& Beta2() { return optimizer.UpdatePolicy().Beta2(); }
//! Get the value used to initialise the mean squared gradient parameter.
double Epsilon() const { return optimizer.UpdatePolicy().Epsilon(); }
//! Modify the value used to initialise the mean 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 QHAdam policy.
SGD<UpdatePolicyType, DecayPolicyType> optimizer;
};
using AdaBound = AdaBoundType<AdaBoundUpdate>;
using AMSBound = AdaBoundType<AMSBoundUpdate>;
} // namespace ens
// Include implementation.
#include "ada_bound_impl.hpp"
#endif
@@ -0,0 +1,47 @@
/**
* @file qhadam_impl.hpp
* @author Niteya Shah
*
* Implementation of QHAdam 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_BOUND_IMPL_HPP
#define ENSMALLEN_ADA_BOUND_IMPL_HPP
// In case it hasn't been included yet.
#include "ada_bound.hpp"
namespace ens {
template<typename UpdatePolicyType, typename DecayPolicyType>
AdaBoundType<UpdatePolicyType, DecayPolicyType>::AdaBoundType(
const double stepSize,
const size_t batchSize,
const double finalLr,
const double gamma,
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,
UpdatePolicyType(finalLr, gamma, epsilon, beta1, beta2),
NoDecay(),
resetPolicy,
exactObjective)
{ /* Nothing to do. */ }
} // namespace ens
#endif
@@ -0,0 +1,207 @@
/**
* @file ada_bound_update.hpp
* @author Marcus Edel
*
* Implments the AdaBound Optimizer. AdaBound is a variant of Adam which
* employs dynamic bounds on learning rates.
*
* 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_BOUND_UPDATE_HPP
#define ENSMALLEN_ADA_BOUND_UPDATE_HPP
namespace ens {
/**
* AdaBound employs dynamic bounds on learning rates to achieve a gradual and
* smooth transition from adaptive methods to SGD.
*
* For more information, see the following.
*
* @code
* @inproceedings{Luo2019AdaBound,
* author = {Luo, Liangchen and Xiong, Yuanhao and Liu, Yan and Sun, Xu},
* title = {Adaptive Gradient Methods with Dynamic Bound of Learning
* Rate},
* booktitle = {Proceedings of the 7th International Conference on Learning
* Representations},
* month = {May},
* year = {2019},
* address = {New Orleans, Louisiana}
* }
* @endcode
*/
class AdaBoundUpdate
{
public:
/**
* Construct the AdaBound update policy with the given parameters.
*
* @param finalLr The final (SGD) learning rate.
* @param gamma The convergence speed of the bound functions.
* @param epsilon The epsilon value used to initialise the squared gradient
* parameter.
* @param beta1 The smoothing parameter.
* @param beta2 The second moment coefficient.
*/
AdaBoundUpdate(const double finalLr = 0.1,
const double gamma = 1e-3,
const double epsilon = 1e-8,
const double beta1 = 0.9,
const double beta2 = 0.999) :
finalLr(finalLr),
gamma(gamma),
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
{
// Nothing to do.
}
//! Get the final (SGD) learning rate.
double FinalLr() const { return finalLr; }
//! Modify the final (SGD) learning rate.
double& FinalLr() { return finalLr; }
//! Get the convergence speed of the bound functions.
double Gamma() const { return finalLr; }
//! Modify the convergence speed of the bound functions.
double& Gamma() { return finalLr; }
//! 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; }
//! Get the smoothing parameter.
double Beta1() const { return beta1; }
//! Modify the smoothing parameter.
double& Beta1() { return beta1; }
//! Get the second moment coefficient.
double Beta2() const { return beta2; }
//! 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent AdaBoundUpdate object.
* @param rows Number of rows in the gradient matrix.
* @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)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
}
/**
* Update step for AdaBound.
*
* @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)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
// Save the initial step size.
if (first)
{
first = false;
initialStepSize = stepSize;
}
// Increment the iteration counter variable.
++parent.iteration;
// Decay the first and second moment running average coefficient.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.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 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));
// Applies bounds on actual learning rate.
iterate -= arma::clamp((stepSize *
std::sqrt(biasCorrection2) / biasCorrection1) / (arma::sqrt(v) +
parent.epsilon), lower, upper) % m;
}
private:
// Instantiated parent object.
AdaBoundUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponential moving average of squared gradient values.
GradType v;
// Whether this is the first call of the Update method.
bool first;
// The initial (Adam) learning rate.
double initialStepSize;
};
private:
// The final (SGD) learning rate.
double finalLr;
// The convergence speed of the bound functions.
double gamma;
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
// The smoothing parameter.
double beta1;
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
#endif
@@ -0,0 +1,214 @@
/**
* @file ams_bound_update.hpp
* @author Marcus Edel
*
* Implments the AMSBound Optimizer. AMSBound is a variant of Adam which
* employs dynamic bounds on learning rates.
*
* 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_AMS_BOUND_UPDATE_HPP
#define ENSMALLEN_AMS_BOUND_UPDATE_HPP
namespace ens {
/**
* AMSBound employs dynamic bounds on learning rates to achieve a gradual and
* smooth transition from adaptive methods to SGD.
*
* For more information, see the following.
*
* @code
* @inproceedings{Luo2019AdaBound,
* author = {Luo, Liangchen and Xiong, Yuanhao and Liu, Yan and Sun, Xu},
* title = {Adaptive Gradient Methods with Dynamic Bound of Learning
* Rate},
* booktitle = {Proceedings of the 7th International Conference on Learning
* Representations},
* month = {May},
* year = {2019},
* address = {New Orleans, Louisiana}
* }
* @endcode
*/
class AMSBoundUpdate
{
public:
/**
* Construct the AMSBound update policy with the given parameters.
*
* @param finalLr The final (SGD) learning rate.
* @param gamma The convergence speed of the bound functions.
* @param epsilon The epsilon value used to initialise the squared gradient
* parameter.
* @param beta1 The smoothing parameter.
* @param beta2 The second moment coefficient.
*/
AMSBoundUpdate(const double finalLr = 0.1,
const double gamma = 1e-3,
const double epsilon = 1e-8,
const double beta1 = 0.9,
const double beta2 = 0.999) :
finalLr(finalLr),
gamma(gamma),
epsilon(epsilon),
beta1(beta1),
beta2(beta2),
iteration(0)
{
// Nothing to do.
}
//! Get the final (SGD) learning rate.
double FinalLr() const { return finalLr; }
//! Modify the final (SGD) learning rate.
double& FinalLr() { return finalLr; }
//! Get the convergence speed of the bound functions.
double Gamma() const { return finalLr; }
//! Modify the convergence speed of the bound functions.
double& Gamma() { return finalLr; }
//! 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; }
//! Get the smoothing parameter.
double Beta1() const { return beta1; }
//! Modify the smoothing parameter.
double& Beta1() { return beta1; }
//! Get the second moment coefficient.
double Beta2() const { return beta2; }
//! 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent AMSBoundUpdate object.
* @param rows Number of rows in the gradient matrix.
* @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)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
vImproved.zeros(rows, cols);
}
/**
* Update step for AMSBound.
*
* @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)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
// Save the initial step size.
if (first)
{
first = false;
initialStepSize = stepSize;
}
// Increment the iteration counter variable.
++parent.iteration;
// Decay the first and second moment running average coefficient.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.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 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));
// Element wise maximum of past and present squared gradients.
vImproved = arma::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;
}
private:
// Instantiated parent object.
AMSBoundUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponential moving average of squared gradient values.
GradType v;
// Whether this is the first call of the Update method.
bool first;
// The initial (Adam) learning rate.
double initialStepSize;
// The optimal squared gradient value.
GradType vImproved;
};
private:
// The final (SGD) learning rate.
double finalLr;
// The convergence speed of the bound functions.
double gamma;
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
// The smoothing parameter.
double beta1;
// The second moment coefficient.
double beta2;
// The number of iterations.
size_t iteration;
};
} // namespace ens
#endif
+40 -6
View File
@@ -66,6 +66,8 @@ class AdaDelta
* 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).
*/
AdaDelta(const double stepSize = 1.0,
const size_t batchSize = 32,
@@ -74,23 +76,50 @@ class AdaDelta
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true);
const bool resetPolicy = true,
const bool exactObjective = false);
/**
* Optimize the given function using AdaDelta. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned. The DecomposableFunctionType is checked for
* objective value is returned. The SeparableFunctionType is checked for
* API consistency at compile time.
*
* @tparam DecomposableFunctionType Type of the function to optimize.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize(function, iterate);
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.
@@ -128,6 +157,11 @@ class AdaDelta
//! 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(); }
@@ -26,7 +26,8 @@ inline AdaDelta::AdaDelta(const double stepSize,
const size_t maxIterations,
const double tolerance,
const bool shuffle,
const bool resetPolicy) :
const bool resetPolicy,
const bool exactObjective) :
optimizer(stepSize,
batchSize,
maxIterations,
@@ -34,7 +35,8 @@ inline AdaDelta::AdaDelta(const double stepSize,
shuffle,
AdaDeltaUpdate(rho, epsilon),
NoDecay(),
resetPolicy)
resetPolicy,
exactObjective)
{ /* Nothing to do. */ }
} // namespace ens
@@ -51,49 +51,6 @@ class AdaDeltaUpdate
// Nothing to do.
}
/**
* The Initialize method is called by SGD Optimizer method before the start of
* the iteration update process. In AdaDelta update policy, the mean squared
* and the delta mean squared gradient matrices are initialized to the zeros
* matrix with the same size as gradient matrix (see ens::SGD<>).
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
// Initialize empty matrices for mean sum of squares of parameter gradient.
meanSquaredGradient = arma::zeros<arma::mat>(rows, cols);
meanSquaredGradientDx = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for SGD. The AdaDelta update dynamically adapts over time using
* only first order information. Additionally, AdaDelta requires no manual
* tuning of a learning rate.
*
* @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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Accumulate gradient.
meanSquaredGradient *= rho;
meanSquaredGradient += (1 - rho) * (gradient % gradient);
arma::mat dx = arma::sqrt((meanSquaredGradientDx + epsilon) /
(meanSquaredGradient + epsilon)) % gradient;
// Accumulate updates.
meanSquaredGradientDx *= rho;
meanSquaredGradientDx += (1 - rho) * (dx % dx);
// Apply update.
iterate -= (stepSize * dx);
}
//! Get the smoothing parameter.
double Rho() const { return rho; }
//! Modify the smoothing parameter.
@@ -104,18 +61,77 @@ class AdaDeltaUpdate
//! Modify the value used to initialise the mean 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:
/**
* This constructor is called by the SGD optimizer method before the start
* of the iteration update process. In AdaDelta update policy, the mean
* squared and the delta mean squared gradient matrices are initialized to
* the zeros matrix with the same size as gradient matrix (see ens::SGD<>).
*
* @param parent AdaDeltaUpdate object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdaDeltaUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
{
meanSquaredGradient.zeros(rows, cols);
meanSquaredGradientDx.zeros(rows, cols);
}
/**
* Update step for SGD. The AdaDelta update dynamically adapts over time
* using only first order information. Additionally, AdaDelta requires no
* manual tuning of a learning rate.
*
* @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)
{
// Accumulate gradient.
meanSquaredGradient *= parent.rho;
meanSquaredGradient += (1 - parent.rho) * (gradient % gradient);
GradType dx = arma::sqrt((meanSquaredGradientDx + parent.epsilon) /
(meanSquaredGradient + parent.epsilon)) % gradient;
// Accumulate updates.
meanSquaredGradientDx *= parent.rho;
meanSquaredGradientDx += (1 - parent.rho) * (dx % dx);
// Apply update.
iterate -= (stepSize * dx);
}
private:
// The instantiated parent class.
AdaDeltaUpdate& parent;
// The mean squared gradient matrix.
GradType meanSquaredGradient;
// The delta mean squared gradient matrix.
GradType meanSquaredGradientDx;
};
private:
// The smoothing parameter.
double rho;
// The epsilon value used to initialise the mean squared gradient parameter.
double epsilon;
// The mean squared gradient matrix.
arma::mat meanSquaredGradient;
// The delta mean squared gradient matrix.
arma::mat meanSquaredGradientDx;
};
} // namespace ens
+39 -5
View File
@@ -64,6 +64,8 @@ class AdaGrad
* 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).
*/
AdaGrad(const double stepSize = 0.01,
const size_t batchSize = 32,
@@ -71,22 +73,49 @@ class AdaGrad
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true);
const bool resetPolicy = true,
const bool exactObjective = false);
/**
* Optimize the given function using AdaGrad. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to optimize.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize(function, iterate);
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.
@@ -119,6 +148,11 @@ class AdaGrad
//! 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(); }
@@ -23,7 +23,8 @@ inline AdaGrad::AdaGrad(const double stepSize,
const size_t maxIterations,
const double tolerance,
const bool shuffle,
const bool resetPolicy) :
const bool resetPolicy,
const bool exactObjective) :
optimizer(stepSize,
batchSize,
maxIterations,
@@ -31,7 +32,8 @@ inline AdaGrad::AdaGrad(const double stepSize,
shuffle,
AdaGradUpdate(epsilon),
NoDecay(),
resetPolicy)
resetPolicy,
exactObjective)
{ /* Nothing to do. */ }
} // namespace ens
@@ -49,49 +49,67 @@ class AdaGradUpdate
// Nothing to do.
}
/**
* The Initialize method is called by SGD Optimizer method before the start of
* the iteration update process. In AdaGrad update policy, squared
* gradient matrix is initialized to the zeros matrix with the same size as
* gradient matrix (see ens::SGD<>).
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
// Initialize an empty matrix for sum of squares of parameter gradient.
squaredGradient = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for SGD. The AdaGrad 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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
squaredGradient += (gradient % gradient);
iterate -= (stepSize * gradient) / (arma::sqrt(squaredGradient) + epsilon);
}
//! 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:
/**
* This constructor is called by the SGD optimizer before the start of the
* iteration update process. In AdaGrad 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(AdaGradUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
squaredGradient(rows, cols)
{
// Initialize an empty matrix for sum of squares of parameter gradient.
squaredGradient.zeros();
}
/**
* Update step for SGD. The AdaGrad 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)
{
squaredGradient += (gradient % gradient);
iterate -= (stepSize * gradient) / (arma::sqrt(squaredGradient) +
parent.epsilon);
}
private:
// Instantiated parent class.
AdaGradUpdate& parent;
// The squared gradient matrix.
GradType squaredGradient;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
// The squared gradient matrix.
arma::mat squaredGradient;
};
} // namespace ens
+40 -6
View File
@@ -11,7 +11,7 @@
* for first-order gradient-based optimization of stochastic objective
* functions, based on adaptive estimates of lower-order moments. AdaMax is
* simply a variant of Adam based on the infinity norm. AMSGrad is another
* variant of Adam with guaranteed convergence. Nadam is another variant of
* variant of Adam with guaranteed convergence. Nadam is another variant of
* Adam based on NAG. NadaMax is a variant for Nadam based on Infinity form.
*
* ensmallen is free software; you may redistribute it and/or modify it under
@@ -88,6 +88,8 @@ class AdamType
* 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).
*/
AdamType(const double stepSize = 0.001,
const size_t batchSize = 32,
@@ -97,22 +99,49 @@ class AdamType
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true);
const bool resetPolicy = true,
const bool exactObjective = false);
/**
* Optimize the given function using Adam. The given starting point will be
* modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to optimize.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize(function, iterate);
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.
@@ -155,6 +184,11 @@ class AdamType
//! 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(); }
+4 -2
View File
@@ -30,7 +30,8 @@ AdamType<UpdateRule>::AdamType(
const size_t maxIterations,
const double tolerance,
const bool shuffle,
const bool resetPolicy) :
const bool resetPolicy,
const bool exactObjective) :
optimizer(stepSize,
batchSize,
maxIterations,
@@ -38,7 +39,8 @@ AdamType<UpdateRule>::AdamType(
shuffle,
UpdateRule(epsilon, beta1, beta2),
NoDecay(),
resetPolicy)
resetPolicy,
exactObjective)
{ /* Nothing to do. */ }
} // namespace ens
+77 -53
View File
@@ -58,52 +58,6 @@ class AdamUpdate
// Nothing to do.
}
/**
* The Initialize method is called by SGD Optimizer method before the start of
* the iteration update process.
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
m = arma::zeros<arma::mat>(rows, cols);
v = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for Adam.
*
* @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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Increment the iteration counter variable.
++iteration;
// And update the iterate.
m *= beta1;
m += (1 - beta1) * gradient;
v *= beta2;
v += (1 - beta2) * (gradient % gradient);
const double biasCorrection1 = 1.0 - std::pow(beta1, iteration);
const double biasCorrection2 = 1.0 - std::pow(beta2, 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) + epsilon);
}
//! 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.
@@ -119,6 +73,82 @@ 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent AdamUpdate object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdamUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
}
/**
* Update step for Adam.
*
* @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.
++parent.iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.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);
/**
* 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);
}
private:
// Instantiated parent object.
AdamUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponential moving average of squared gradient values.
GradType v;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
@@ -129,14 +159,8 @@ class AdamUpdate
// The second moment coefficient.
double beta2;
// The exponential moving average of gradient values.
arma::mat m;
// The exponential moving average of squared gradient values.
arma::mat v;
// The number of iterations.
double iteration;
size_t iteration;
};
} // namespace ens
+69 -48
View File
@@ -60,47 +60,6 @@ class AdaMaxUpdate
// Nothing to do.
}
/**
* The Initialize method is called by SGD Optimizer method before the start of
* the iteration update process.
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
m = arma::zeros<arma::mat>(rows, cols);
u = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for Adam.
*
* @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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Increment the iteration counter variable.
++iteration;
// And update the iterate.
m *= beta1;
m += (1 - beta1) * gradient;
// Update the exponentially weighted infinity norm.
u *= beta2;
u = arma::max(u, arma::abs(gradient));
const double biasCorrection1 = 1.0 - std::pow(beta1, iteration);
if (biasCorrection1 != 0)
iterate -= (stepSize / biasCorrection1 * m / (u + epsilon));
}
//! 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.
@@ -116,6 +75,74 @@ 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent AdaMaxUpdate object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(AdaMaxUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
{
m.zeros(rows, cols);
u.zeros(rows, cols);
}
/**
* Update step for AdaMax.
*
* @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.
++parent.iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
// Update the exponentially weighted infinity norm.
u *= parent.beta2;
u = arma::max(u, arma::abs(gradient));
const double biasCorrection1 = 1.0 - std::pow(parent.beta1,
parent.iteration);
if (biasCorrection1 != 0)
iterate -= (stepSize / biasCorrection1 * m / (u + parent.epsilon));
}
private:
// Instantiated parent object.
AdaMaxUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponentially weighted infinity norm.
GradType u;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
@@ -126,14 +153,8 @@ class AdaMaxUpdate
// The second moment coefficient.
double beta2;
// The exponential moving average of gradient values.
arma::mat m;
// The exponentially weighted infinity norm.
arma::mat u;
// The number of iterations.
double iteration;
size_t iteration;
};
} // namespace ens
+79 -55
View File
@@ -53,51 +53,6 @@ class AMSGradUpdate
// Nothing to do.
}
/**
* The Initialize method is called by SGD Optimizer method before the start of
* the iteration update process.
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
m = arma::zeros<arma::mat>(rows, cols);
v = arma::zeros<arma::mat>(rows, cols);
vImproved = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for AMSGrad.
*
* @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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Increment the iteration counter variable.
++iteration;
// And update the iterate.
m *= beta1;
m += (1 - beta1) * gradient;
v *= beta2;
v += (1 - beta2) * (gradient % gradient);
const double biasCorrection1 = 1.0 - std::pow(beta1, iteration);
const double biasCorrection2 = 1.0 - std::pow(beta2, iteration);
// Element wise maximum of past and present squared gradients.
vImproved = arma::max(vImproved, v);
iterate -= (stepSize * std::sqrt(biasCorrection2) / biasCorrection1) *
m / (arma::sqrt(vImproved) + epsilon);
}
//! 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.
@@ -113,6 +68,84 @@ 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent Instantiated AMSGradUpdate parent object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(AMSGradUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
vImproved.zeros(rows, cols);
}
/**
* Update step for AMSGrad.
*
* @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.
++parent.iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.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);
// Element wise maximum of past and present squared gradients.
vImproved = arma::max(vImproved, v);
iterate -= (stepSize * std::sqrt(biasCorrection2) / biasCorrection1) *
m / (arma::sqrt(vImproved) + parent.epsilon);
}
private:
// Instantiated parent AMSGradUpdate object.
AMSGradUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponential moving average of squared gradient values.
GradType v;
// The optimal squared gradient value.
GradType vImproved;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
@@ -123,17 +156,8 @@ class AMSGradUpdate
// The second moment coefficient.
double beta2;
// The exponential moving average of gradient values.
arma::mat m;
// The exponential moving average of squared gradient values.
arma::mat v;
// The optimal sqaured gradient value.
arma::mat vImproved;
// The number of iterations.
double iteration;
size_t iteration;
};
} // namespace ens
+92 -74
View File
@@ -51,78 +51,16 @@ class NadamUpdate
beta1(beta1),
beta2(beta2),
scheduleDecay(scheduleDecay),
iteration(0),
cumBeta1(1)
iteration(0)
{
// Nothing to do.
}
/**
* The Initialize() method is called by the optimizer before the start of the
* iteration update process.
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
m = arma::zeros<arma::mat>(rows, cols);
v = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for Nadam.
*
* @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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Increment the iteration counter variable.
++iteration;
// And update the iterate.
m *= beta1;
m += (1 - beta1) * gradient;
v *= beta2;
v += (1 - beta2) * gradient % gradient;
double beta1T = beta1 * (1 - (0.5 *
std::pow(0.96, iteration * scheduleDecay)));
double beta1T1 = beta1 * (1 - (0.5 *
std::pow(0.96, (iteration + 1) * scheduleDecay)));
cumBeta1 *= beta1T;
const double biasCorrection1 = 1.0 - cumBeta1;
const double biasCorrection2 = 1.0 - std::pow(beta2, iteration);
const double biasCorrection3 = 1.0 - (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) + epsilon);
}
//! 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; }
//! Get the value of the cumulative product of decay coefficients
double CumBeta1() const { return cumBeta1; }
//! Modify the value of the cumulative product of decay coefficients
double& CumBeta1() { return cumBeta1; }
//! Get the smoothing parameter.
double Beta1() const { return beta1; }
//! Modify the smoothing parameter.
@@ -138,6 +76,95 @@ 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the optimizer before the start of the
* iteration update process.
*
* @param parent Instantiated NadamUpdate parent object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(NadamUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
cumBeta1(1)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
}
/**
* Update step for Nadam.
*
* @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.
++parent.iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * gradient % gradient;
double beta1T = parent.beta1 * (1 - (0.5 *
std::pow(0.96, parent.iteration * parent.scheduleDecay)));
double beta1T1 = parent.beta1 * (1 - (0.5 *
std::pow(0.96, (parent.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);
/* 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);
}
private:
// Instantiated parent object.
NadamUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponential moving average of squared gradient values.
GradType v;
// The cumulative product of decay coefficients.
double cumBeta1;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
@@ -148,20 +175,11 @@ class NadamUpdate
// The second moment coefficient.
double beta2;
// The exponential moving average of gradient values.
arma::mat m;
// The exponential moving average of squared gradient values.
arma::mat v;
// The decay parameter for decay coefficients
// The decay parameter for decay coefficients.
double scheduleDecay;
// The number of iterations.
double iteration;
// The cumulative product of decay coefficients
double cumBeta1;
size_t iteration;
};
} // namespace ens
+86 -69
View File
@@ -51,74 +51,16 @@ class NadaMaxUpdate
beta1(beta1),
beta2(beta2),
scheduleDecay(scheduleDecay),
cumBeta1(1),
iteration(0)
{
// Nothing to do.
}
/**
* The Initialize() method is called by the optimizer before the start of the
* iteration update process.
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
m = arma::zeros<arma::mat>(rows, cols);
u = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for NadaMax.
*
* @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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Increment the iteration counter variable.
++iteration;
// And update the iterate.
m *= beta1;
m += (1 - beta1) * gradient;
u = arma::max(u * beta2, arma::abs(gradient));
double beta1T = beta1 * (1 - (0.5 *
std::pow(0.96, iteration * scheduleDecay)));
double beta1T1 = beta1 * (1 - (0.5 *
std::pow(0.96, (iteration + 1) * scheduleDecay)));
cumBeta1 *= beta1T;
const double biasCorrection1 = 1.0 - cumBeta1;
const double biasCorrection2 = 1.0 - (cumBeta1 * beta1T1);
if ((biasCorrection1 != 0) && (biasCorrection2 != 0))
{
iterate -= (stepSize * (((1 - beta1T) / biasCorrection1) * gradient
+ (beta1T1 / biasCorrection2) * m)) / (u + epsilon);
}
}
//! 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; }
//! Get the value of the cumulative product of decay coefficients
double CumBeta1() const { return cumBeta1; }
//! Modify the value of the cumulative product of decay coefficients
double& CumBeta1() { return cumBeta1; }
//! Get the smoothing parameter.
double Beta1() const { return beta1; }
//! Modify the smoothing parameter.
@@ -134,6 +76,90 @@ 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor method is called by the optimizer before the start of
* the iteration update process.
*
* @param parent Instantiated NadaMaxUpdate parent object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(NadaMaxUpdate& parent, const size_t rows, const size_t cols) :
parent(parent),
cumBeta1(1)
{
m.zeros(rows, cols);
u.zeros(rows, cols);
}
/**
* Update step for NadaMax.
*
* @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.
++parent.iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
u = arma::max(u * parent.beta2, arma::abs(gradient));
double beta1T = parent.beta1 * (1 - (0.5 *
std::pow(0.96, parent.iteration * parent.scheduleDecay)));
double beta1T1 = parent.beta1 * (1 - (0.5 *
std::pow(0.96, (parent.iteration + 1) * parent.scheduleDecay)));
cumBeta1 *= beta1T;
const double biasCorrection1 = 1.0 - cumBeta1;
const double biasCorrection2 = 1.0 - (cumBeta1 * beta1T1);
if ((biasCorrection1 != 0) && (biasCorrection2 != 0))
{
iterate -= (stepSize * (((1 - beta1T) / biasCorrection1) * gradient
+ (beta1T1 / biasCorrection2) * m)) / (u + parent.epsilon);
}
}
private:
// Instantiated parent object.
NadaMaxUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponentially weighted infinity norm.
GradType u;
// The cumulative product of decay coefficients.
double cumBeta1;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
@@ -144,20 +170,11 @@ class NadaMaxUpdate
// The second moment coefficient.
double beta2;
// The exponential moving average of gradient values.
arma::mat m;
// The exponentially weighted infinity norm.
arma::mat u;
// The decay parameter for decay coefficients
// The decay parameter for decay coefficients.
double scheduleDecay;
// The cumulative product of decay coefficients
double cumBeta1;
// The number of iterations.
double iteration;
size_t iteration;
};
} // namespace ens
@@ -2,7 +2,7 @@
* @file optimisticadam_update.hpp
* @author Moksh Jain
*
* OptmisticAdam optimizer. Implements Optimistic Adam, an algorithm which
* OptmisticAdam optimizer. Implements Optimistic Adam, an algorithm which
* uses Optimistic Mirror Descent with the Adam optimizer.
*
* ensmallen is free software; you may redistribute it and/or modify it under
@@ -16,18 +16,18 @@
namespace ens {
/**
* OptimisticAdam is an optimizer which implements the Optimistic Adam
* OptimisticAdam is an optimizer which implements the Optimistic Adam
* algorithm which uses Optmistic Mirror Descent with the Adam Optimizer.
* It addresses the problem of limit cycling while training GANs. It uses
* OMD to achieve faster regret rates in solving the zero sum game of
* training a GAN. It consistently achieves a smaller KL divergnce with
* OMD to achieve faster regret rates in solving the zero sum game of
* training a GAN. It consistently achieves a smaller KL divergnce with
* respect to the true underlying data distribution.
*
* For more information, see the following.
*
* @code
* @article{
* author = {Constantinos Daskalakis, Andrew Ilyas, Vasilis Syrgkanis,
* author = {Constantinos Daskalakis, Andrew Ilyas, Vasilis Syrgkanis,
* Haoyang Zeng},
* title = {Training GANs with Optimism},
* year = {2017},
@@ -57,51 +57,6 @@ class OptimisticAdamUpdate
// Nothing to do.
}
/**
* The Initialize method is called by SGD Optimizer method before the start of
* the iteration update process.
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
m = arma::zeros<arma::mat>(rows, cols);
v = arma::zeros<arma::mat>(rows, cols);
g = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for OptimisticAdam.
*
* @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(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Increment the iteration counter variable.
++iteration;
// And update the iterate.
m *= beta1;
m += (1 - beta1) * gradient;
v *= beta2;
v += (1 - beta2) * arma::square(gradient);
arma::mat mCorrected = m / (1.0 - std::pow(beta1, iteration));
arma::mat vCorrected = v / (1.0 - std::pow(beta2, iteration));
arma::mat update = mCorrected / (arma::sqrt(vCorrected) + epsilon);
iterate -= (2 * stepSize * update - stepSize * g);
g = std::move(update);
}
//! Get the value used to initialize the squared gradient parameter.
double Epsilon() const { return epsilon; }
//! Modify the value used to initialize the squared gradient parameter.
@@ -117,6 +72,85 @@ 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent Instantiated OptimisticAdamUpdate parent object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(OptimisticAdamUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
{
m.zeros(rows, cols);
v.zeros(rows, cols);
g.zeros(rows, cols);
}
/**
* Update step for OptimisticAdam.
*
* @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.
++parent.iteration;
// And update the iterate.
m *= parent.beta1;
m += (1 - parent.beta1) * gradient;
v *= parent.beta2;
v += (1 - parent.beta2) * arma::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 update = mCorrected /
(arma::sqrt(vCorrected) + parent.epsilon);
iterate -= (2 * stepSize * update - stepSize * g);
g = std::move(update);
}
private:
// Instantiated parent object.
OptimisticAdamUpdate& parent;
// The exponential moving average of gradient values.
GradType m;
// The exponential moving average of squared gradient values.
GradType v;
// The previous update.
GradType g;
};
private:
// The epsilon value used to initialize the squared gradient parameter.
double epsilon;
@@ -127,16 +161,8 @@ class OptimisticAdamUpdate
// The second moment coefficient.
double beta2;
// The exponential moving average of gradient values.
arma::mat m;
// The exponential moving average of squared gradient values.
arma::mat v;
// The previous update.
arma::mat g;
// The number of iterations.
double iteration;
size_t iteration;
};
} // namespace ens
@@ -55,12 +55,34 @@ class AugLagrangian
*
* @tparam LagrangianFunctionType Function which can be optimized by this
* class.
* @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 The function to optimize.
* @param coordinates Output matrix to store the optimized coordinates in.
* @param callbacks Callback functions.
*/
template<typename LagrangianFunctionType>
template<typename LagrangianFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
Optimize(LagrangianFunctionType& function,
MatType& coordinates,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename LagrangianFunctionType,
typename MatType,
typename... CallbackTypes>
bool Optimize(LagrangianFunctionType& function,
arma::mat& coordinates);
MatType& coordinates,
CallbackTypes&&... callbacks)
{
return Optimize<LagrangianFunctionType, MatType, MatType,
CallbackTypes...>(function, coordinates,
std::forward<CallbackTypes>(callbacks)...);
}
/**
* Optimize the function, giving initial estimates for the Lagrange
@@ -69,17 +91,41 @@ class AugLagrangian
*
* @tparam LagrangianFunctionType Function which can be optimized by this
* class.
* @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 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 callbacks Callback functions.
*/
template<typename LagrangianFunctionType>
template<typename LagrangianFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
Optimize(LagrangianFunctionType& function,
MatType& coordinates,
const arma::vec& initLambda,
const double initSigma,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename LagrangianFunctionType,
typename MatType,
typename... CallbackTypes>
bool Optimize(LagrangianFunctionType& function,
arma::mat& coordinates,
MatType& coordinates,
const arma::vec& initLambda,
const double initSigma);
const double initSigma,
CallbackTypes&&... callbacks)
{
return Optimize<LagrangianFunctionType, MatType, MatType,
CallbackTypes...>(function, coordinates, initLambda, initSigma,
std::forward<CallbackTypes>(callbacks)...);
}
//! Get the L-BFGS object used for the actual optimization.
const L_BFGS& LBFGS() const { return lbfgs; }
@@ -124,8 +170,12 @@ class AugLagrangian
//! The L-BFGS optimizer that we will use.
L_BFGS lbfgs;
//! Controls early termination of the optimization process.
bool terminate;
//! Lagrange multipliers.
arma::vec lambda;
//! Penalty parameter.
double sigma;
@@ -133,9 +183,27 @@ class AugLagrangian
* Internal optimization function: given an initialized AugLagrangianFunction,
* perform the optimization itself.
*/
template<typename LagrangianFunctionType>
bool Optimize(AugLagrangianFunction<LagrangianFunctionType>& augfunc,
arma::mat& coordinates);
template<typename LagrangianFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
Optimize(AugLagrangianFunction<LagrangianFunctionType>& augfunc,
MatType& coordinates,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename LagrangianFunctionType,
typename MatType,
typename... CallbackTypes>
bool Optimize(AugLagrangianFunction<LagrangianFunctionType>& function,
MatType& coordinates,
CallbackTypes&&... callbacks)
{
return Optimize<LagrangianFunctionType, MatType, MatType,
CallbackTypes...>(function, coordinates,
std::forward<CallbackTypes>(callbacks)...);
}
};
} // namespace ens
@@ -63,7 +63,8 @@ class AugLagrangianFunction
* @param coordinates Coordinates to evaluate function at.
* @return Objective function.
*/
double Evaluate(const arma::mat& coordinates) const;
template<typename MatType>
typename MatType::elem_type Evaluate(const MatType& coordinates) const;
/**
* Evaluate the gradient of the Augmented Lagrangian function.
@@ -71,7 +72,8 @@ class AugLagrangianFunction
* @param coordinates Coordinates to evaluate gradient at.
* @param gradient Matrix to store gradient into.
*/
void Gradient(const arma::mat& coordinates, arma::mat& gradient) const;
template<typename MatType, typename GradType>
void Gradient(const MatType& coordinates, GradType& gradient) const;
/**
* Get the initial point of the optimization (supplied by the
@@ -79,7 +81,8 @@ class AugLagrangianFunction
*
* @return Initial point.
*/
const arma::mat& GetInitialPoint() const;
template<typename MatType = arma::mat>
const MatType& GetInitialPoint() const;
//! Get the Lagrange multipliers.
const arma::vec& Lambda() const { return lambda; }
@@ -46,19 +46,22 @@ AugLagrangianFunction<LagrangianFunction>::AugLagrangianFunction(
// Evaluate the AugLagrangianFunction at the given coordinates.
template<typename LagrangianFunction>
double AugLagrangianFunction<LagrangianFunction>::Evaluate(
const arma::mat& coordinates) const
template<typename MatType>
typename MatType::elem_type AugLagrangianFunction<LagrangianFunction>::Evaluate(
const MatType& coordinates) const
{
// The augmented Lagrangian is evaluated as
// f(x) + {-lambda_i * c_i(x) + (sigma / 2) c_i(x)^2} for all constraints
typedef typename MatType::elem_type ElemType;
// First get the function's objective value.
double objective = function.Evaluate(coordinates);
ElemType objective = function.Evaluate(coordinates);
// Now loop for each constraint.
for (size_t i = 0; i < function.NumConstraints(); ++i)
{
double constraint = function.EvaluateConstraint(i, coordinates);
ElemType constraint = function.EvaluateConstraint(i, coordinates);
objective += (-lambda[i] * constraint) +
sigma * std::pow(constraint, 2) / 2;
@@ -69,22 +72,23 @@ double AugLagrangianFunction<LagrangianFunction>::Evaluate(
// Evaluate the gradient of the AugLagrangianFunction at the given coordinates.
template<typename LagrangianFunction>
template<typename MatType, typename GradType>
void AugLagrangianFunction<LagrangianFunction>::Gradient(
const arma::mat& coordinates,
arma::mat& gradient) const
const MatType& coordinates,
GradType& gradient) const
{
// The augmented Lagrangian's gradient is evaluted as
// f'(x) + {(-lambda_i + sigma * c_i(x)) * c'_i(x)} for all constraints
gradient.zeros();
function.Gradient(coordinates, gradient);
arma::mat constraintGradient; // Temporary for constraint gradients.
GradType constraintGradient; // Temporary for constraint gradients.
for (size_t i = 0; i < function.NumConstraints(); i++)
{
function.GradientConstraint(i, coordinates, constraintGradient);
// Now calculate scaling factor and add to existing gradient.
arma::mat tmpGradient;
GradType tmpGradient;
tmpGradient = (-lambda[i] + sigma *
function.EvaluateConstraint(i, coordinates)) * constraintGradient;
gradient += tmpGradient;
@@ -93,10 +97,11 @@ void AugLagrangianFunction<LagrangianFunction>::Gradient(
// Get the initial point.
template<typename LagrangianFunction>
const arma::mat& AugLagrangianFunction<LagrangianFunction>::GetInitialPoint()
template<typename MatType>
const MatType& AugLagrangianFunction<LagrangianFunction>::GetInitialPoint()
const
{
return function.GetInitialPoint();
return function.template GetInitialPoint<MatType>();
}
} // namespace ens
@@ -26,15 +26,22 @@ inline AugLagrangian::AugLagrangian(const size_t maxIterations,
maxIterations(maxIterations),
penaltyThresholdFactor(penaltyThresholdFactor),
sigmaUpdateFactor(sigmaUpdateFactor),
lbfgs(lbfgs)
lbfgs(lbfgs),
terminate(false),
sigma(0.0)
{
}
template<typename LagrangianFunctionType>
bool AugLagrangian::Optimize(LagrangianFunctionType& function,
arma::mat& coordinates,
const arma::vec& initLambda,
const double initSigma)
template<typename LagrangianFunctionType,
typename MatType,
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)
{
lambda = initLambda;
sigma = initSigma;
@@ -42,46 +49,79 @@ bool AugLagrangian::Optimize(LagrangianFunctionType& function,
AugLagrangianFunction<LagrangianFunctionType> augfunc(function,
lambda, sigma);
return Optimize(augfunc, coordinates);
return Optimize(augfunc, coordinates, callbacks...);
}
template<typename LagrangianFunctionType>
bool AugLagrangian::Optimize(LagrangianFunctionType& function,
arma::mat& coordinates)
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)
{
// If the user did not specify the right size for sigma and lambda, we will
// use defaults.
if (!lambda.is_empty())
{
AugLagrangianFunction<LagrangianFunctionType> augfunc(function, lambda, sigma);
return Optimize(augfunc, coordinates);
AugLagrangianFunction<LagrangianFunctionType> augfunc(function, lambda,
sigma);
return Optimize(augfunc, coordinates, callbacks...);
}
else
{
AugLagrangianFunction<LagrangianFunctionType> augfunc(function);
return Optimize(augfunc, coordinates);
return Optimize(augfunc, coordinates, callbacks...);
}
}
template<typename LagrangianFunctionType>
bool AugLagrangian::Optimize(
template<typename LagrangianFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value, bool>::type
AugLagrangian::Optimize(
AugLagrangianFunction<LagrangianFunctionType>& augfunc,
arma::mat& coordinates)
MatType& coordinatesIn,
CallbackTypes&&... callbacks)
{
traits::CheckConstrainedFunctionTypeAPI<LagrangianFunctionType>();
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
BaseMatType& coordinates = (BaseMatType&) coordinatesIn;
// Check that the types satisfy our needs.
traits::CheckConstrainedFunctionTypeAPI<LagrangianFunctionType, BaseMatType,
BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
LagrangianFunctionType& function = augfunc.Function();
// Ensure that we update lambda immediately.
double penaltyThreshold = DBL_MAX;
ElemType penaltyThreshold = std::numeric_limits<ElemType>::max();
// Track the last objective to compare for convergence.
double lastObjective = function.Evaluate(coordinates);
ElemType lastObjective = function.Evaluate(coordinates);
// Convergence tolerance---depends on the epsilon of the type we are using for
// optimization.
ElemType tolerance = 1e3 * std::numeric_limits<ElemType>::epsilon();
// Then, calculate the current penalty.
double penalty = 0;
ElemType penalty = 0;
for (size_t i = 0; i < function.NumConstraints(); i++)
penalty += std::pow(function.EvaluateConstraint(i, coordinates), 2);
{
const ElemType p = std::pow(function.EvaluateConstraint(i, coordinates), 2);
Callback::EvaluateConstraint(*this, function, coordinates, i, p,
callbacks...);
penalty += p;
}
Info << "Penalty is " << penalty << " (threshold " << penaltyThreshold
<< ")." << std::endl;
@@ -89,36 +129,51 @@ bool AugLagrangian::Optimize(
// The odd comparison allows user to pass maxIterations = 0 (i.e. no limit on
// number of iterations).
size_t it;
for (it = 0; it != (maxIterations - 1); it++)
terminate |= 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))
if (!lbfgs.Optimize(augfunc, coordinates, callbacks...))
Info << "L-BFGS reported an error during optimization."
<< std::endl;
Info << "Done with L-BFGS: " << coordinates << "\n";
const ElemType objective = function.Evaluate(coordinates);
Callback::Evaluate(*this, function, coordinates, objective,
callbacks...);
// Check if we are done with the entire optimization (the threshold we are
// comparing with is arbitrary).
if (std::abs(lastObjective - function.Evaluate(coordinates)) < 1e-10 &&
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;
}
lastObjective = function.Evaluate(coordinates);
lastObjective = objective;
// Assuming that the optimization has converged to a new set of coordinates,
// we now update either lambda or sigma. We update sigma if the penalty
// term is too high, and we update lambda otherwise.
// First, calculate the current penalty.
double penalty = 0;
ElemType penalty = 0;
for (size_t i = 0; i < function.NumConstraints(); i++)
{
penalty += std::pow(function.EvaluateConstraint(i, coordinates), 2);
const ElemType p = std::pow(function.EvaluateConstraint(i, coordinates),
2);
Callback::EvaluateConstraint(*this, function, coordinates, i, p,
callbacks...);
penalty += p;
}
Info << "Penalty is " << penalty << " (threshold "
@@ -129,8 +184,13 @@ bool AugLagrangian::Optimize(
// We use the update: lambda_{k + 1} = lambda_k - sigma * c(coordinates),
// but we have to write a loop to do this for each constraint.
for (size_t i = 0; i < function.NumConstraints(); i++)
augfunc.Lambda()[i] -= augfunc.Sigma() *
function.EvaluateConstraint(i, coordinates);
{
const ElemType p = function.EvaluateConstraint(i, coordinates);
Callback::EvaluateConstraint(*this, function, coordinates, i, p,
callbacks...);
augfunc.Lambda()[i] -= augfunc.Sigma() * p;
}
// We also update the penalty threshold to be a factor of the current
// penalty.
@@ -142,9 +202,20 @@ bool AugLagrangian::Optimize(
// We multiply sigma by a constant value.
augfunc.Sigma() *= sigmaUpdateFactor;
Info << "Updated sigma to " << augfunc.Sigma() << "." << std::endl;
if (augfunc.Sigma() >= std::numeric_limits<ElemType>::max() / 2.0)
{
Warn << "AugLagrangian::Optimize(): sigma too large for element type; "
<< "terminating." << std::endl;
Callback::EndOptimization(*this, function, coordinates, callbacks...);
return false;
}
}
terminate |= Callback::StepTaken(*this, function, coordinates,
callbacks...);
}
Callback::EndOptimization(*this, function, coordinates, callbacks...);
return false;
}
@@ -54,112 +54,6 @@ class AdaptiveStepsize
searchParameter(searchParameter)
{ /* Nothing to do here. */ }
/**
* This function is called in each iteration.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @param function Function to be optimized (minimized).
* @param stepSize Step size to be used for the given iteration.
* @param iterate Parameters that minimize the function.
* @param gradient The gradient matrix.
* @param gradientNorm The gradient norm to be used for the given iteration.
* @param offset The batch offset to be used for the given iteration.
* @param batchSize Batch size to be used for the given iteration.
* @param backtrackingBatchSize Backtracking batch size to be used for the
* given iteration.
* @param reset Reset the step size decay parameter.
*/
template<typename DecomposableFunctionType>
void Update(DecomposableFunctionType& function,
double& stepSize,
arma::mat& iterate,
arma::mat& gradient,
double& gradientNorm,
double& sampleVariance,
const size_t offset,
const size_t batchSize,
const size_t backtrackingBatchSize,
const bool /* reset */)
{
Backtracking(function, stepSize, iterate, gradient, gradientNorm, offset,
backtrackingBatchSize);
// Update the iterate.
iterate -= stepSize * gradient;
// Update Gradient & calculate curvature of quadratic approximation.
arma::mat functionGradient(iterate.n_rows, iterate.n_cols);
arma::mat gradPrevIterate(iterate.n_rows, iterate.n_cols);
arma::mat functionGradientPrev(iterate.n_rows, iterate.n_cols);
double vB = 0;
arma::mat delta0, delta1;
// Initialize previous iterate, if not already initialized.
if (iteratePrev.is_empty())
{
iteratePrev.zeros(iterate.n_rows, iterate.n_cols);
}
// Compute the stochastic gradient estimation.
function.Gradient(iterate, offset, gradient, 1);
function.Gradient(iteratePrev, offset, gradPrevIterate, 1);
delta1 = gradient;
for (size_t j = 1, k = 1; j < backtrackingBatchSize; ++j, ++k)
{
function.Gradient(iterate, offset + j, functionGradient, 1);
delta0 = delta1 + (functionGradient - delta1) / k;
// Compute sample variance.
vB += arma::norm(functionGradient - delta1, 2.0) *
arma::norm(functionGradient - delta0, 2.0);
delta1 = delta0;
gradient += functionGradient;
// Used for curvature calculation.
function.Gradient(iteratePrev, offset + j, functionGradientPrev, 1);
gradPrevIterate += functionGradientPrev;
}
// Update sample variance & norm of the gradient.
sampleVariance = vB;
gradientNorm = std::pow(arma::norm(gradient / backtrackingBatchSize, 2), 2.0);
// Compute curvature.
double v = arma::trace(arma::trans(iterate - iteratePrev) *
(gradient - gradPrevIterate)) /
std::pow(arma::norm(iterate - iteratePrev, 2), 2.0);
// Update previous iterate.
iteratePrev = iterate;
// TODO: Develop an absolute strategy to deal with stepSizeDecay updates in
// case we arrive at local minima. See #1469 for more details.
double stepSizeDecay = 0;
if (gradientNorm && sampleVariance && batchSize)
{
if (batchSize < function.NumFunctions())
{
stepSizeDecay = (1 - (1 / ((double) batchSize - 1) * sampleVariance) /
(batchSize * gradientNorm)) / v;
}
else
{
stepSizeDecay = 1 / v;
}
}
// Stepsize smoothing.
stepSize *= (1 - ((double) batchSize / function.NumFunctions()));
stepSize += stepSizeDecay * ((double) batchSize / function.NumFunctions());
Backtracking(function, stepSize, iterate, gradient, gradientNorm, offset,
backtrackingBatchSize);
}
//! Get the backtracking step size.
double BacktrackStepSize() const { return backtrackStepSize; }
//! Modify the backtracking step size.
@@ -170,51 +64,177 @@ class AdaptiveStepsize
//! Modify the search parameter.
double& SearchParameter() { return searchParameter; }
private:
/**
* Definition of the backtracking line search algorithm based on the
* ArmijoGoldstein condition to determine the maximum amount to move along
* the given search direction.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @param function Function to be optimized (minimized).
* @param stepSize Step size to be used for the given iteration.
* @param iterate Parameters that minimize the function.
* @param gradient The gradient matrix.
* @param gradientNorm The gradient norm to be used for the given iteration.
* @param offset The batch offset to be used for the given iteration.
* @param backtrackingBatchSize The backtracking batch size.
*/
template<typename DecomposableFunctionType>
void Backtracking(DecomposableFunctionType& function,
double& stepSize,
const arma::mat& iterate,
const arma::mat& gradient,
const double gradientNorm,
const size_t offset,
const size_t backtrackingBatchSize)
template<typename MatType>
class Policy
{
double overallObjective = function.Evaluate(iterate, offset,
backtrackingBatchSize);
public:
// Create the instantiated object.
Policy(AdaptiveStepsize& parent) : parent(parent) { }
arma::mat iterateUpdate = iterate - (stepSize * gradient);
double overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
backtrackingBatchSize);
while (overallObjectiveUpdate >
(overallObjective - searchParameter * stepSize * gradientNorm))
/**
* This function is called in each iteration.
*
* @tparam SeparableFunctionType Type of the function to be optimized.
* @param function Function to be optimized (minimized).
* @param stepSize Step size to be used for the given iteration.
* @param iterate Parameters that minimize the function.
* @param gradient The gradient matrix.
* @param gradientNorm The gradient norm to be used for the given iteration.
* @param offset The batch offset to be used for the given iteration.
* @param batchSize Batch size to be used for the given iteration.
* @param backtrackingBatchSize Backtracking batch size to be used for the
* given iteration.
* @param reset Reset the step size decay parameter.
*/
template<typename SeparableFunctionType,
typename GradType>
void Update(SeparableFunctionType& function,
double& stepSize,
MatType& iterate,
GradType& gradient,
double& gradientNorm,
double& sampleVariance,
const size_t offset,
const size_t batchSize,
const size_t backtrackingBatchSize,
const bool /* reset */)
{
stepSize *= backtrackStepSize;
Backtracking(function, stepSize, iterate, gradient, gradientNorm, offset,
backtrackingBatchSize);
iterateUpdate = iterate - (stepSize * gradient);
overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
// Update the iterate.
iterate -= stepSize * gradient;
// Update Gradient & calculate curvature of quadratic approximation.
GradType functionGradient(iterate.n_rows, iterate.n_cols);
GradType gradPrevIterate(iterate.n_rows, iterate.n_cols);
GradType functionGradientPrev(iterate.n_rows, iterate.n_cols);
double vB = 0;
GradType delta0, delta1;
// Initialize previous iterate, if not already initialized.
if (iteratePrev.is_empty())
{
iteratePrev.zeros(iterate.n_rows, iterate.n_cols);
}
// Compute the stochastic gradient estimation.
function.Gradient(iterate, offset, gradient, 1);
function.Gradient(iteratePrev, offset, gradPrevIterate, 1);
delta1 = gradient;
for (size_t j = 1, k = 1; j < backtrackingBatchSize; ++j, ++k)
{
function.Gradient(iterate, offset + j, functionGradient, 1);
delta0 = delta1 + (functionGradient - delta1) / k;
// Compute sample variance.
vB += arma::norm(functionGradient - delta1, 2.0) *
arma::norm(functionGradient - delta0, 2.0);
delta1 = delta0;
gradient += functionGradient;
// Used for curvature calculation.
function.Gradient(iteratePrev, offset + j, functionGradientPrev, 1);
gradPrevIterate += functionGradientPrev;
}
// Update sample variance & norm of the gradient.
sampleVariance = vB;
gradientNorm = std::pow(arma::norm(gradient / backtrackingBatchSize, 2),
2.0);
// Compute curvature.
double v = arma::trace(arma::trans(iterate - iteratePrev) *
(gradient - gradPrevIterate)) /
std::pow(arma::norm(iterate - iteratePrev, 2), 2.0);
// Update previous iterate.
iteratePrev = iterate;
// TODO: Develop an absolute strategy to deal with stepSizeDecay updates
// in case we arrive at local minima. See #1469 for more details.
double stepSizeDecay = 0;
if (gradientNorm && sampleVariance && batchSize)
{
if (batchSize < function.NumFunctions())
{
stepSizeDecay = (1 - (1 / ((double) batchSize - 1) * sampleVariance) /
(batchSize * gradientNorm)) / v;
}
else
{
stepSizeDecay = 1 / v;
}
}
// Stepsize smoothing.
stepSize *= (1 - ((double) batchSize / function.NumFunctions()));
stepSize += stepSizeDecay * ((double) batchSize /
function.NumFunctions());
Backtracking(function, stepSize, iterate, gradient, gradientNorm, offset,
backtrackingBatchSize);
}
}
//! Last function parameters value.
arma::mat iteratePrev;
/**
* Definition of the backtracking line search algorithm based on the
* ArmijoGoldstein condition to determine the maximum amount to move along
* the given search direction.
*
* @tparam SeparableFunctionType Type of the function to be optimized.
* @param function Function to be optimized (minimized).
* @param stepSize Step size to be used for the given iteration.
* @param iterate Parameters that minimize the function.
* @param gradient The gradient matrix.
* @param gradientNorm The gradient norm to be used for the given iteration.
* @param offset The batch offset to be used for the given iteration.
* @param backtrackingBatchSize The backtracking batch size.
*/
template<typename SeparableFunctionType,
typename GradType>
void Backtracking(SeparableFunctionType& function,
double& stepSize,
const MatType& iterate,
const GradType& gradient,
const double gradientNorm,
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);
ElemType overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
backtrackingBatchSize);
while (overallObjectiveUpdate >
(overallObjective - parent.searchParameter * stepSize *
gradientNorm))
{
stepSize *= parent.backtrackStepSize;
iterateUpdate = iterate - (stepSize * gradient);
overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
backtrackingBatchSize);
}
}
private:
//! Reference to parent.
AdaptiveStepsize& parent;
//! Last function parameters value.
MatType iteratePrev;
};
private:
//! The backtracking step size for each iteration.
double backtrackStepSize;
@@ -51,53 +51,74 @@ class BacktrackingLineSearch
searchParameter(searchParameter)
{ /* Nothing to do here. */ }
/**
* This function is called in each iteration.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @param function Function to be optimized (minimized).
* @param stepSize Step size to be used for the given iteration.
* @param iterate Parameters that minimize the function.
* @param gradient The gradient matrix.
* @param gradientNorm The gradient norm to be used for the given iteration.
* @param offset The batch offset to be used for the given iteration.
* @param batchSize Batch size to be used for the given iteration.
* @param backtrackingBatchSize Backtracking batch size to be used for the
* given iteration.
* @param reset Reset the step size decay parameter.
*/
template<typename DecomposableFunctionType>
void Update(DecomposableFunctionType& function,
double& stepSize,
arma::mat& iterate,
const arma::mat& gradient,
const double gradientNorm,
const double /* sampleVariance */,
const size_t offset,
const size_t /* batchSize */,
const size_t backtrackingBatchSize,
const bool reset)
//! Get the search parameter.
double SearchParameter() const { return searchParameter; }
//! Modify the search parameter.
double& SearchParameter() { return searchParameter; }
template<typename MatType>
class Policy
{
if (reset)
stepSize *= 2;
public:
// Instantiate the policy with the given parent.
Policy(BacktrackingLineSearch& parent) : parent(parent) { }
double overallObjective = function.Evaluate(iterate, offset,
backtrackingBatchSize);
arma::mat iterateUpdate = iterate - (stepSize * gradient);
double overallObjectiveUpdate = function.Evaluate(iterateUpdate,
offset, backtrackingBatchSize);
while (overallObjectiveUpdate >
(overallObjective - searchParameter * stepSize * gradientNorm))
/**
* This function is called in each iteration.
*
* @tparam SeparableFunctionType Type of the function to be optimized.
* @param function Function to be optimized (minimized).
* @param stepSize Step size to be used for the given iteration.
* @param iterate Parameters that minimize the function.
* @param gradient The gradient matrix.
* @param gradientNorm The gradient norm to be used for the given iteration.
* @param offset The batch offset to be used for the given iteration.
* @param batchSize Batch size to be used for the given iteration.
* @param backtrackingBatchSize Backtracking batch size to be used for the
* given iteration.
* @param reset Reset the step size decay parameter.
*/
template<typename SeparableFunctionType,
typename GradType>
void Update(SeparableFunctionType& function,
double& stepSize,
MatType& iterate,
GradType& gradient,
double& gradientNorm,
double& /* sampleVariance */,
const size_t offset,
const size_t /* batchSize */,
const size_t backtrackingBatchSize,
const bool reset)
{
stepSize /= 2;
if (reset)
stepSize *= 2;
iterateUpdate = iterate - (stepSize * gradient);
overallObjectiveUpdate = function.Evaluate(iterateUpdate,
offset, backtrackingBatchSize);
typedef typename MatType::elem_type ElemType;
ElemType overallObjective = function.Evaluate(iterate, offset,
backtrackingBatchSize);
MatType iterateUpdate = iterate - (stepSize * gradient);
ElemType overallObjectiveUpdate = function.Evaluate(iterateUpdate, offset,
backtrackingBatchSize);
while (overallObjectiveUpdate >
(overallObjective - parent.searchParameter * stepSize *
gradientNorm))
{
stepSize /= 2;
iterateUpdate = iterate - (stepSize * gradient);
overallObjectiveUpdate = function.Evaluate(iterateUpdate,
offset, backtrackingBatchSize);
}
}
}
private:
//! Reference to instantiated parent object.
BacktrackingLineSearch& parent;
};
private:
//! The search parameter for each iteration.
@@ -58,7 +58,7 @@ namespace ens {
* title = {Big Batch {SGD:} Automated Inference using Adaptive Batch
* Sizes},
* author = {Soham De and Abhay Kumar Yadav and David W. Jacobs and
Tom Goldstein},
* Tom Goldstein},
* journal = {CoRR},
* year = {2017},
* url = {http://arxiv.org/abs/1610.05792},
@@ -91,26 +91,58 @@ class BigBatchSGD
* @param tolerance Maximum absolute tolerance to terminate algorithm.
* @param shuffle If true, the batch order is shuffled; otherwise, each
* batch is visited in linear order.
* @param exactObjective Calculate the exact objective (Default: estimate the
* final objective obtained on the last pass over the data).
*/
BigBatchSGD(const size_t batchSize = 1000,
const double stepSize = 0.01,
const double batchDelta = 0.1,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true);
const bool shuffle = true,
const bool exactObjective = false);
/**
* Clean any memory associated with the BigBatchSGD object.
*/
~BigBatchSGD();
/**
* Optimize the given function using big-batch SGD. The given starting point
* will be modified to store the finishing point of the algorithm, and the
* final objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function,
arma::mat& iterate);
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
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 batch size.
size_t BatchSize() const { return batchSize; }
@@ -147,6 +179,11 @@ class BigBatchSGD
//! Modify the update policy.
UpdatePolicyType& UpdatePolicy() { return updatePolicy; }
//! Get whether or not the actual objective is calculated.
bool ExactObjective() const { return exactObjective; }
//! Modify whether or not the actual objective is calculated.
bool& ExactObjective() { return exactObjective; }
private:
//! The size of the current batch.
size_t batchSize;
@@ -167,8 +204,14 @@ class BigBatchSGD
//! iterating.
bool shuffle;
//! Controls whether or not the actual Objective value is calculated.
bool exactObjective;
//! The update policy used to update the parameters in each iteration.
UpdatePolicyType updatePolicy;
//! Instantiated update policy.
Any instUpdatePolicy;
};
using BBS_Armijo = BigBatchSGD<BacktrackingLineSearch>;
@@ -26,76 +26,88 @@ BigBatchSGD<UpdatePolicyType>::BigBatchSGD(
const double batchDelta,
const size_t maxIterations,
const double tolerance,
const bool shuffle) :
const bool shuffle,
const bool exactObjective) :
batchSize(batchSize),
stepSize(stepSize),
batchDelta(batchDelta),
maxIterations(maxIterations),
tolerance(tolerance),
shuffle(shuffle),
exactObjective(exactObjective),
updatePolicy(UpdatePolicyType())
{ /* Nothing to do. */ }
template<typename UpdatePolicyType>
BigBatchSGD<UpdatePolicyType>::~BigBatchSGD()
{
instUpdatePolicy.Clean();
}
//! Optimize the function (minimize).
template<typename UpdatePolicyType>
template<typename DecomposableFunctionType>
double BigBatchSGD<UpdatePolicyType>::Optimize(
DecomposableFunctionType& function, arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
BigBatchSGD<UpdatePolicyType>::Optimize(
SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
typedef Function<DecomposableFunctionType> FullFunctionType;
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
typedef Function<SeparableFunctionType, BaseMatType, BaseGradType>
FullFunctionType;
FullFunctionType& f(static_cast<FullFunctionType&>(function));
// Make sure we have all the methods that we need.
traits::CheckDecomposableFunctionTypeAPI<FullFunctionType>();
traits::CheckSeparableFunctionTypeAPI<FullFunctionType, BaseMatType,
BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
BaseMatType& iterate = (BaseMatType&) iterateIn;
typedef typename UpdatePolicyType::template Policy<BaseMatType>
InstUpdatePolicyType;
if (!instUpdatePolicy.Has<InstUpdatePolicyType>())
{
instUpdatePolicy.Clean();
instUpdatePolicy.Set<InstUpdatePolicyType>(
new InstUpdatePolicyType(updatePolicy));
}
// Find the number of functions to use.
const size_t numFunctions = f.NumFunctions();
// To keep track of where we are and how things are going.
size_t currentFunction = 0;
double overallObjective = 0;
double lastObjective = DBL_MAX;
size_t epoch = 1;
ElemType overallObjective = 0;
ElemType lastObjective = DBL_MAX;
bool reset = false;
arma::mat delta0, delta1;
BaseGradType delta0, delta1;
// Controls early termination of the optimization process.
bool terminate = false;
// Now iterate!
arma::mat gradient(iterate.n_rows, iterate.n_cols);
arma::mat functionGradient(iterate.n_rows, iterate.n_cols);
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
BaseGradType functionGradient(iterate.n_rows, iterate.n_cols);
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
for (size_t i = 0; i < actualMaxIterations; /* incrementing done manually */)
terminate |= Callback::BeginOptimization(*this, f, iterate, callbacks...);
for (size_t i = 0; i < actualMaxIterations && !terminate;
/* incrementing done manually */)
{
// Is this iteration the start of a sequence?
if ((currentFunction % numFunctions) == 0 && i > 0)
{
// Output current objective function.
Info << "Big-batch SGD: iteration " << i << ", objective "
<< overallObjective << "." << std::endl;
if (std::isnan(overallObjective) || std::isinf(overallObjective))
{
Warn << "Big-batch SGD: converged to " << overallObjective
<< "; terminating with failure. Try a smaller step size?"
<< std::endl;
return overallObjective;
}
if (std::abs(lastObjective - overallObjective) < tolerance)
{
Info << "Big-batch SGD: minimized within tolerance " << tolerance
<< "; terminating optimization." << std::endl;
return overallObjective;
}
// Reset the counter variables.
lastObjective = overallObjective;
overallObjective = 0;
currentFunction = 0;
if (shuffle) // Determine order of visitation.
f.Shuffle();
}
// Find the effective batch size; we have to take the minimum of three
// things:
// - the batch size can't be larger than the user-specified batch size;
@@ -112,10 +124,16 @@ double BigBatchSGD<UpdatePolicyType>::Optimize(
// Compute the stochastic gradient estimation.
f.Gradient(iterate, currentFunction, gradient, 1);
terminate |= Callback::Gradient(*this, f, iterate, gradient, callbacks...);
delta1 = gradient;
for (size_t j = 1; j < effectiveBatchSize; ++j, ++k)
{
f.Gradient(iterate, currentFunction + j, functionGradient, 1);
terminate |= Callback::Gradient(*this, f, iterate, functionGradient,
callbacks...);
delta0 = delta1 + (functionGradient - delta1) / k;
// Compute sample variance.
@@ -150,6 +168,9 @@ double BigBatchSGD<UpdatePolicyType>::Optimize(
for (size_t j = 0; j < batchOffset; ++j, ++k)
{
f.Gradient(iterate, batchStart + j, functionGradient, 1);
terminate |= Callback::Gradient(*this, f, iterate,
functionGradient, callbacks...);
delta0 = delta1 + (functionGradient - delta1) / (k + 1);
// Compute sample variance.
@@ -170,29 +191,83 @@ double BigBatchSGD<UpdatePolicyType>::Optimize(
}
}
updatePolicy.Update(f, stepSize, iterate, gradient, gB, vB,
currentFunction, batchSize, effectiveBatchSize, reset);
instUpdatePolicy.As<InstUpdatePolicyType>().Update(f, stepSize, iterate,
gradient, gB, vB, currentFunction, batchSize, effectiveBatchSize,
reset);
// Update the iterate.
iterate -= stepSize * gradient;
terminate |= Callback::StepTaken(*this, f, iterate, callbacks...);
overallObjective += f.Evaluate(iterate, currentFunction,
const ElemType objective = f.Evaluate(iterate, currentFunction,
effectiveBatchSize);
overallObjective += objective;
terminate |= Callback::Evaluate(*this, f, iterate, objective,
callbacks...);
i += effectiveBatchSize;
currentFunction += effectiveBatchSize;
// Is this iteration the start of a sequence?
if ((currentFunction % numFunctions) == 0)
{
terminate |= Callback::EndEpoch(*this, f, iterate, epoch++,
overallObjective / (ElemType) numFunctions, callbacks...);
// Output current objective function.
Info << "Big-batch SGD: iteration " << i << ", objective "
<< overallObjective << "." << std::endl;
if (std::isnan(overallObjective) || std::isinf(overallObjective))
{
Warn << "Big-batch SGD: converged to " << overallObjective
<< "; terminating with failure. Try a smaller step size?"
<< std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
if (std::abs(lastObjective - overallObjective) < tolerance ||
Callback::BeginEpoch(*this, f, iterate, epoch, overallObjective,
callbacks...))
{
Info << "Big-batch SGD: minimized within tolerance " << tolerance
<< "; terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
// Reset the counter variables.
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;
// Calculate final objective.
overallObjective = 0;
for (size_t i = 0; i < numFunctions; i += batchSize)
// Calculate final objective if exactObjective is set to true.
if (exactObjective)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - i);
overallObjective += f.Evaluate(iterate, i, effectiveBatchSize);
overallObjective = 0;
for (size_t i = 0; i < numFunctions; i += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - i);
const ElemType objective = f.Evaluate(iterate, i, effectiveBatchSize);
overallObjective += objective;
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
}
}
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
@@ -0,0 +1,776 @@
/**
* @file callbacks.hpp
* @author Marcus Edel
*
* The Callback class will invoke the specified callbacks.
*
* 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_CALLBACKS_HPP
#define ENSMALLEN_CALLBACKS_CALLBACKS_HPP
#include <ensmallen_bits/callbacks/traits.hpp>
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):
* called after any call to Evaluate().
*
* - StepTaken(optimizer, function, coordinates):
* called after any step is taken.
*
* - Gradient(optimizer, function, coordinates, gradient):
* called whenever the gradient is computed.
*
* - 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):
* called after any call to EvaluateConstraint().
*
* - GradientConstraint(optimizer, function, coordinates, constraint,
* constraintGradient):
* called after any call to GradientConstraint().
*
* - BeginOptimization(optimizer, function, coordinates):
* called at the beginning of the optimization.
*
* - EndOptimization(optimizer, function, coordinates):
* called at the end of the optimization.
*/
class Callback
{
public:
/**
* Invoke the BeginOptimization() callback if it exists.
*
* @param callback The callback to call.
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasBeginOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasBool,
bool>::type
BeginOptimizationFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates)
{
return const_cast<CallbackType&>(callback).BeginOptimization(optimizer,
function, coordinates);
}
template<typename CallbackType,
typename OptimizerType,
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
BeginOptimizationFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ return false; }
/**
* Iterate over the callbacks and invoke the BeginOptimization() callback if
* it exists.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename... CallbackTypes>
static bool BeginOptimization(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates,
CallbackTypes&... callbacks)
{
// 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;
}
/**
* Invoke the EndOptimization() callback if it exists.
*
* @param callback The callback to call.
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasEndOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
EndOptimizationFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates)
{
return (const_cast<CallbackType&>(callback).EndOptimization(
optimizer, function, coordinates), false);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<!callbacks::traits::HasEndOptimizationSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
EndOptimizationFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ return false; }
/**
* Iterate over the callbacks and invoke the EndOptimization() callback if it
* exists.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename... CallbackTypes>
static bool EndOptimization(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates,
CallbackTypes&... callbacks)
{
// 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;
}
/**
* Invoke the Evaluate() callback if it exists.
*
* @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 objective Objective value of the current point.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasEvaluateSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
EvaluateFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const double objective)
{
return (const_cast<CallbackType&>(callback).Evaluate(
optimizer, function, coordinates, objective), false);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<!callbacks::traits::HasEvaluateSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
EvaluateFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const double /* objective */)
{ return false; }
/**
* Iterate over the callbacks and invoke the Evaluate() callback if it exists.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objective Objective value of the current point.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename... CallbackTypes>
static bool Evaluate(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const double objective,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)(objective); // prevent spurious compiler warnings
(void)std::initializer_list<bool>{ result =
result || Callback::EvaluateFunction(callbacks, optimizer, function,
coordinates, objective)... };
return result;
}
/**
* Invoke the EvaluateConstraint() callback if it exists.
*
* @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 constraint The index of the constraint.
* @param constraintValue Constraint value of the current point.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasEvaluateConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
EvaluateConstraintFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraint,
const double constraintValue)
{
return (const_cast<CallbackType&>(callback).EvaluateConstraint(
optimizer, function, coordinates, constraint, constraintValue), false);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
!callbacks::traits::HasEvaluateConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value,
bool>::type
EvaluateConstraintFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* constraint */,
const double /* constraintValue */)
{ return false; }
/**
* Iterate over the callbacks and invoke the EvaluateConstraint() callback if
* it exists.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param constraint The index of the constraint.
* @param constraintValue Constraint value of the current point.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename... CallbackTypes>
static bool EvaluateConstraint(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraint,
const double constraintValue,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)(constraint); // prevent spurious compiler warnings
(void)(constraintValue);
(void)std::initializer_list<bool>{ result =
result || Callback::EvaluateConstraintFunction(callbacks, optimizer,
function, coordinates, constraint, constraintValue)... };
return result;
}
/**
* Invoke the Gradient() callback if it exists.
*
* @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 gradient Matrix that holds the gradient.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
static typename std::enable_if<callbacks::traits::HasGradientSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::value,
bool>::type
GradientFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
GradType& gradient)
{
return (const_cast<CallbackType&>(callback).Gradient(
optimizer, function, coordinates, gradient), 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>::value,
bool>::type
GradientFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
GradType& /* gradient */)
{ return false; }
/**
* Iterate over the callbacks and invoke the Gradient() callback if it exists.
*
* @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.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
static bool Gradient(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
GradType& gradient,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)std::initializer_list<bool>{ result =
result || Callback::GradientFunction(callbacks, optimizer, function,
coordinates, gradient)... };
return result;
}
/**
* Invoke the GradientConstraint() callback if it exists.
*
* @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 gradient Matrix that holds the gradient.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
static typename std::enable_if<
callbacks::traits::HasGradientConstraintSignature<
CallbackType, OptimizerType, FunctionType, MatType, GradType>::value,
bool>::type
GradientConstraintFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraint,
GradType& gradient)
{
return (const_cast<CallbackType&>(callback).GradientConstraint(
optimizer, function, coordinates, constraint, gradient), 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>::value,
bool>::type
GradientConstraintFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* constraint */,
GradType& /* gradient */)
{ return false; }
/**
* Iterate over the callbacks and invoke the GradientConstraint() callback if
* it exists.
*
* @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.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
static bool Gradient(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t constraint,
GradType& gradient,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)(constraint); // prevent spurious compiler warnings
(void)std::initializer_list<bool>{ result =
result || Callback::GradientConstraintFunction(callbacks, optimizer,
function, coordinates, constraint, gradient)... };
return result;
}
/**
* Iterate over the callbacks and invoke the Evaluate() and Gradient()
* callback if it exists.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objective Objective value of the current point.
* @param gradient Matrix that holds the gradient.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
static bool EvaluateWithGradient(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const double objective,
GradType& gradient,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)(objective); // prevent spurious compiler warnings
(void)std::initializer_list<bool>{ result =
result || Callback::EvaluateFunction(callbacks, optimizer, function,
coordinates, objective)... };
(void)std::initializer_list<bool>{ result =
result || Callback::GradientFunction(callbacks, optimizer, function,
coordinates, gradient)... };
return result;
}
/**
* Invoke the BeginEpoch() callback if it exists.
*
* @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 epoch The index of the current epoch.
* @param objective Objective value of the current point.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasBeginEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value, bool>::type
BeginEpochFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective)
{
return (const_cast<CallbackType&>(callback).BeginEpoch(
optimizer, function, coordinates, epoch, objective), false);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<!callbacks::traits::HasBeginEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::value, bool>::type
BeginEpochFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double /* objective */)
{ return false; }
/**
* Iterate over all callbacks and invoke the BeginEpoch() callback if it
* exists.
*
* @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.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename... CallbackTypes>
static bool BeginEpoch(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)(epoch); // prevent spurious compiler warnings
(void)(objective);
(void)std::initializer_list<bool>{ result =
result || Callback::BeginEpochFunction(callbacks, optimizer, function,
coordinates, epoch, objective)... };
return result;
}
/**
* Invoke the EndEpoch() callback if it exists.
*
* @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 epoch The index of the current epoch.
* @param objective Objective value of the current point.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasEndEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasBool, bool>::type
EndEpochFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective)
{
return const_cast<CallbackType&>(callback).EndEpoch(
optimizer, function, coordinates, epoch, objective);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasEndEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasVoid, bool>::type
EndEpochFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective)
{
const_cast<CallbackType&>(callback).EndEpoch(
optimizer, function, coordinates, epoch, objective);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<callbacks::traits::HasEndEpochSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasNone, bool>::type
EndEpochFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double /* objective */)
{ return false; }
/**
* Iterate over all callbacks and invoke the EndEpoch() callback if it exists.
*
* @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.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename... CallbackTypes>
static bool EndEpoch(OptimizerType& optimizer,
FunctionType& function,
const MatType& coordinates,
const size_t epoch,
const double objective,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)(epoch); // prevent spurious compiler warnings
(void)(objective);
(void)std::initializer_list<bool>{ result =
result || Callback::EndEpochFunction(callbacks, optimizer, function,
coordinates, epoch, objective)... };
return result;
}
/**
* Invoke the StepTaken() callback if it exists.
*
* @param callback The callback to call.
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
*/
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasStepTakenSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasBool,
bool>::type
StepTakenFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates)
{
return const_cast<CallbackType&>(callback).StepTaken(optimizer,
function, coordinates);
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasStepTakenSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasVoid,
bool>::type
StepTakenFunction(CallbackType& callback,
OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates)
{
const_cast<CallbackType&>(callback).StepTaken(optimizer, function,
coordinates);
return false;
}
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
static typename std::enable_if<
callbacks::traits::HasStepTakenSignature<
CallbackType, OptimizerType, FunctionType, MatType>::hasNone,
bool>::type
StepTakenFunction(CallbackType& /* callback */,
OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ return false; }
/**
* Iterate over the callbacks and invoke the StepTaken() callback if it
* exists.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param callbacks The callbacks container.
*/
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename... CallbackTypes>
static bool StepTaken(OptimizerType& optimizer,
FunctionType& function,
MatType& coordinates,
CallbackTypes&... callbacks)
{
// This will return immediately once a callback returns true.
bool result = false;
(void)std::initializer_list<bool>{ result =
result || Callback::StepTakenFunction(callbacks, optimizer,
function, coordinates)... };
return result;
}
};
} // namespace ens
#endif
@@ -0,0 +1,127 @@
/**
* @file early_stop_at_min_loss.hpp
* @author Marcus Edel
* @author Omar Shrit
*
* Implementation of the early stop at minimum loss 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_EARLY_STOP_AT_MIN_LOSS_HPP
#define ENSMALLEN_CALLBACKS_EARLY_STOP_AT_MIN_LOSS_HPP
#include <functional>
namespace ens {
/**
* Early stopping to terminate the optimization process early if the loss stops
* decreasing.
*/
template<typename MatType = arma::mat>
class EarlyStopAtMinLossType
{
public:
/**
* Set up the early stop at min loss class, which keeps track of the minimum
* loss and stops the optimization process if the loss stops decreasing.
*
* @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),
bestObjective(std::numeric_limits<double>::max()),
steps(0)
{ /* Nothing to do here */ }
/**
* Set up the early stop at min loss class, which keeps track of the minimum
* loss and stops the optimization process if the loss stops decreasing.
*
* @param func, callback to return immediate loss evaluated by the function
* @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>(
std::function<double(const MatType&)> func,
const size_t patienceIn = 10)
: callbackUsed(true),
patience(patienceIn),
bestObjective(std::numeric_limits<double>::max()),
steps(0),
localFunc(func)
{
// Nothing to do here
}
/**
* Callback function called at the end of a pass over the data.
*
* @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>
bool EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& coordinates,
const size_t /* epoch */,
double objective)
{
if (callbackUsed)
{
objective = localFunc(coordinates);
}
if (objective < bestObjective)
{
steps = 0;
bestObjective = objective;
return false;
}
steps++;
if (steps >= patience)
{
Info << "Minimum loss reached; terminate optimization." << std::endl;
return true;
}
return false;
}
private:
//! 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.
size_t patience;
//! Locally-stored best objective.
double bestObjective;
//! Locally-stored number of steps since the loss improved.
size_t steps;
//! Function to call at the end of the epoch.
std::function<double(const MatType&)> localFunc;
};
/*
* Note that the using definition is temporary, this definition should
* be removed when releasing ensmallen 3.0
* The renaming of the class is only to avoid a major version bump
* because if the template type added to this class
*/
using EarlyStopAtMinLoss = EarlyStopAtMinLossType<arma::mat>;
} // namespace ens
#endif
@@ -0,0 +1,57 @@
/**
* @file print_loss.hpp
* @author Marcus Edel
*
* Implementation of the print loss 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_PRINT_LOSS_HPP
#define ENSMALLEN_CALLBACKS_PRINT_LOSS_HPP
namespace ens {
/**
* Print loss function, based on the EndEpoch callback function.
*/
class PrintLoss
{
public:
/**
* Set up the print loss callback class with the width and output stream.
*
* @param ostream Ostream which receives output from this object.
*/
PrintLoss(std::ostream& output = arma::get_cout_stream()) : output(output)
{ /* Nothing to do here. */ }
/**
* Callback function called at the end of a pass over the data.
*
* @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>
void EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double objective)
{
output << objective << std::endl;
}
private:
//! The output stream that all data is to be sent to; example: std::cout.
std::ostream& output;
};
} // namespace ens
#endif
@@ -0,0 +1,263 @@
/**
* @file progress_bar.hpp
* @author Marcus Edel
*
* Implementation of a simple progress bar 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_PROGRESS_BAR_HPP
#define ENSMALLEN_CALLBACKS_PROGRESS_BAR_HPP
#include <ensmallen_bits/function.hpp>
namespace ens {
/**
* A simple progress bar, based on the maximum number of optimizer iterations,
* batch-size, number of functions and the StepTaken callback function.
*/
class ProgressBar
{
public:
/**
* Set up the progress bar callback class with the given width and output
* stream.
*
* @param widthIn Width of the bar.
* @param ostream Ostream which receives output from this object.
*/
ProgressBar(const size_t widthIn = 70,
std::ostream& output = arma::get_cout_stream()) :
width(100.0 / widthIn),
output(output),
objective(0),
epochs(0),
epochSize(0),
step(1),
steps(0),
newEpoch(false),
epoch(1)
{ /* Nothing to do here. */ }
/**
* Callback function called at the begin of the optimization process.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginOptimization(OptimizerType& optimizer,
FunctionType& function,
MatType& /* coordinates */)
{
static_assert(traits::HasBatchSizeSignature<
OptimizerType>::value,
"The OptimizerType does not have a correct definition of BatchSize(). "
"Please check that the OptimizerType fully satisfies the requirements "
"of the ProgressBar API; see the callbacks documentation for more "
"details.");
static_assert(traits::HasMaxIterationsSignature<
OptimizerType>::value,
"The OptimizerType does not have a correct definition of MaxIterations()."
" Please check that the OptimizerType fully satisfies the requirements "
"of the ProgressBar API; see the callbacks documentation for more "
"details.");
static_assert(traits::HasNumFunctionsSignature<
FunctionType>::value,
"The OptimizerType does not have a correct definition of NumFunctions(). "
"Please check that the OptimizerType fully satisfies the requirements "
"of the ProgressBar API; see the callbacks documentation for more "
"details.");
epochSize = function.NumFunctions() / optimizer.BatchSize();
if (function.NumFunctions() % optimizer.BatchSize() > 0)
epochSize++;
epochs = optimizer.MaxIterations() / function.NumFunctions();
if (optimizer.MaxIterations() % function.NumFunctions() > 0)
epochs++;
stepTimer.tic();
}
/**
* Callback function called at the begin of a pass over the data.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param epochIn The index of the current epoch.
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t epochIn,
const double /* objective */)
{
// Start the timer.
epochTimer.tic();
// Reset epoch parameter.
objective = 0;
step = 1;
epoch = epochIn;
newEpoch = true;
}
/**
* Callback function called once a step is taken.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void StepTaken(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */)
{
if (newEpoch)
{
output << "Epoch " << epoch;
if (epochs > 0)
{
output << "/" << epochs;
}
output << '\n';
newEpoch = false;
}
const size_t progress = ((double) step / epochSize) * 100;
output << step++ << "/" << epochSize << " [";
for (size_t i = 0; i < 100; i += width)
{
if (i < progress)
{
output << "=";
}
else if (i == progress)
{
output << ">";
}
else
{
output << ".";
}
}
output << "] " << progress << "% - ETA: " << (size_t) (stepTimer.toc() *
(epochSize - step + 1)) % 60 << "s - loss: " <<
objective / (double) step << "\r";
output.flush();
stepTimer.tic();
}
/**
* Callback function called at any call to Evaluate().
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objectiveIn Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void Evaluate(OptimizerType& optimizer,
FunctionType& /* function */,
const MatType& /* coordinates */,
const double objectiveIn)
{
objective += objectiveIn / optimizer.BatchSize();
steps++;
}
/**
* Callback function called at the end of a pass over the data.
*
* @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>
void EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double objective)
{
const size_t progress = ((double) (step - 1) / epochSize) * 100;
output << step - 1 << "/" << epochSize << " [";
for (size_t i = 0; i < 100; i += width)
{
if (i < progress)
{
output << "=";
}
else if (i == progress)
{
output << ">";
}
else
{
output << ".";
}
}
const size_t stepTime = epochTimer.toc() / (double) epochSize * 1000;
output << "] " << progress << "% - " << (size_t) epochTimer.toc() % 60
<< "s " << stepTime << "ms/step " << "- loss: " << objective << "\n";
output.flush();
}
private:
//! Length of a single step (1%).
double width;
//! The output stream that all data is to be sent to; example: std::cout.
std::ostream& output;
//! Objective over the current epoch.
double objective;
//! Total number of epochs
size_t epochs;
//! Number of steps per epoch.
size_t epochSize;
//! Current step number.
size_t step;
//! Number of steps taken.
size_t steps;
//! Indicates a new epoch.
bool newEpoch;
//! Locally-stored epoch.
size_t epoch;
//! Locally-stored step timer object.
arma::wall_clock stepTimer;
//! Locally-stored epoch timer object.
arma::wall_clock epochTimer;
};
} // namespace ens
#endif
@@ -0,0 +1,75 @@
/**
* @file store_best_coordinates.hpp
* @author Marcus Edel
*
* Implementation of the store best coordinates 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_STORE_BEST_COORDINATES_HPP
#define ENSMALLEN_CALLBACKS_STORE_BEST_COORDINATES_HPP
namespace ens {
/**
* Store best coordinates function, based on the Evaluate callback function.
*
* @tparam MatType Type of the model coordinates (arma::colvec, arma::mat,
* arma::sp_mat or arma::cube).
*/
template<typename ModelMatType = arma::mat>
class StoreBestCoordinates
{
public:
/**
* Set up the store best model class, which keeps the best-performing
* coordinates and objective.
*/
StoreBestCoordinates() : bestObjective(std::numeric_limits<double>::max())
{ /* Nothing to do here. */ }
/**
* Callback function called after any call to Evaluate().
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
* @param objective Objective value of the current point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void Evaluate(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& coordinates,
const double objective)
{
if (objective < bestObjective)
{
bestObjective = objective;
bestCoordinates = coordinates;
}
}
//! Get the best coordinates.
ModelMatType const& BestCoordinates() const { return bestCoordinates; }
//! Modify the best coordinates.
ModelMatType& BestCoordinatesl() { return bestCoordinates; }
//! Get the best objective.
double const& BestObjective() const { return bestObjective; }
//! Modify the best objective.
double& BestObjective() { return bestObjective; }
private:
//! Locally-stored best objective.
double bestObjective;
//! Locally-stored best model coordinates.
ModelMatType bestCoordinates;
};
} // namespace ens
#endif
@@ -0,0 +1,83 @@
/**
* @file timer_stop.hpp
* @author Marcus Edel
*
* Implementation of the timer stop 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_TIMER_STOP_HPP
#define ENSMALLEN_CALLBACKS_TIMER_STOP_HPP
namespace ens {
/**
* Timer stop function, is based on the BeginOptimization callback function to
* start the timer and the EndEpoch callback function to update the timer.
*/
class TimerStop
{
public:
/**
* Set up the print loss callback class with the width and output stream.
*
* @param durationIn The duration of the timer in seconds.
*/
TimerStop(const double durationIn) : duration(durationIn)
{ /* Nothing to do here. */ }
/**
* Callback function called at the start of the optimization process.
*
* @param optimizer The optimizer used to update the function.
* @param function Function to optimize.
* @param coordinates Starting point.
*/
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginOptimization(OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{
// Start the timer.
timer.tic();
}
/**
* Callback function called at the end of a pass over the data.
*
* @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 */,
const size_t /* epoch */,
const double /* objective */)
{
if (timer.toc() > duration)
{
Info << "Timer timeout reached; terminate optimization." << std::endl;
return true;
}
return false;
}
private:
//! The duration in seconds.
double duration;
//! Locally-stored timer object.
arma::wall_clock timer;
};
} // namespace ens
#endif
+372
View File
@@ -0,0 +1,372 @@
/**
* @file traits.hpp
* @author Marcus Edel
*
* This file provides metaprogramming utilities for detecting certain members of
* CallbackType classes.
*
* 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_TRAITS_HPP
#define ENSMALLEN_CALLBACKS_TRAITS_HPP
#include <ensmallen_bits/function/sfinae_utility.hpp>
namespace ens {
namespace callbacks {
namespace traits {
//! Detect an Evaluate() method.
ENS_HAS_EXACT_METHOD_FORM(Evaluate, HasEvaluate)
//! Detect an EvaluateConstraint() method.
ENS_HAS_EXACT_METHOD_FORM(EvaluateConstraint, HasEvaluateConstraint)
//! Detect an Gradient() method.
ENS_HAS_EXACT_METHOD_FORM(Gradient, HasGradient)
//! Detect an GradientConstraint() method.
ENS_HAS_EXACT_METHOD_FORM(GradientConstraint, HasGradientConstraint)
//! Detect an BeginOptimization() method.
ENS_HAS_EXACT_METHOD_FORM(BeginOptimization, HasBeginOptimization)
//! Detect an EndOptimization() method.
ENS_HAS_EXACT_METHOD_FORM(EndOptimization, HasEndOptimization)
//! Detect an BeginEpoch() method.
ENS_HAS_EXACT_METHOD_FORM(BeginEpoch, HasBeginEpoch)
//! Detect an EndEpoch() method.
ENS_HAS_EXACT_METHOD_FORM(EndEpoch, HasEndEpoch)
//! Detect an StepTaken() method.
ENS_HAS_EXACT_METHOD_FORM(StepTaken, HasStepTaken)
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType = MatType>
struct TypedForms
{
//! This is the form of a bool Evaluate() callback method.
template<typename CallbackType>
using EvaluateBoolForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const double);
//! This is the form of a void Evaluate() callback method.
template<typename CallbackType>
using EvaluateVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const double);
//! This is the form of a bool EvaluateConstraint() callback method.
template<typename CallbackType>
using EvaluateConstraintBoolForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const double);
//! This is the form of a void EvaluateConstraint() callback method.
template<typename CallbackType>
using EvaluateConstraintVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const double);
//! This is the form of a bool Gradient() callback method.
template<typename CallbackType>
using GradientBoolForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const MatType&);
//! This is the form of a void Gradient() callback method.
template<typename CallbackType>
using GradientVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const MatType&);
//! This is the form of a bool GradientConstraint() callback method.
template<typename CallbackType>
using GradientConstraintBoolForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const MatType&);
//! This is the form of a void GradientConstraint() callback method.
template<typename CallbackType>
using GradientConstraintVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const MatType&);
//! This is the form of a bool BeginOptimization() callback method.
template<typename CallbackType>
using BeginOptimizationBoolForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
MatType&);
//! This is the form of a void BeginOptimization() callback method.
template<typename CallbackType>
using BeginOptimizationVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
MatType&);
//! This is the form of a bool EndOptimization() callback method.
template<typename CallbackType>
using EndOptimizationBoolForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
MatType&);
//! This is the form of a void EndOptimization() callback method.
template<typename CallbackType>
using EndOptimizationVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
MatType&);
//! This is the form of a bool BeginEpoch() callback method.
template<typename CallbackType>
using BeginEpochBoolForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const double);
//! This is the form of a void BeginEpoch() callback method.
template<typename CallbackType>
using BeginEpochVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const double);
//! This is the form of a bool EndEpoch() callback method.
template<typename CallbackType>
using EndEpochBoolForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const double);
//! This is the form of a void EndEpoch() callback method.
template<typename CallbackType>
using EndEpochVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&,
const size_t,
const double);
//! This is the form of a bool StepTaken() callback method.
template<typename CallbackType>
using StepTakenBoolForm =
bool(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&);
//! This is the form of a void StepTaken() callback method.
template<typename CallbackType>
using StepTakenVoidForm =
void(CallbackType::*)(OptimizerType&,
FunctionType&,
const MatType&);
};
//! Utility struct, check if either void BeginOptimization() or
//! bool BeginOptimization() exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
struct HasBeginOptimizationSignature
{
const static bool hasBool =
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()
//! exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
struct HasEvaluateSignature
{
const static bool value =
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
//! bool EvaluateConstraint() exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
struct HasEvaluateConstraintSignature
{
const static bool value =
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()
//! exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename Gradient>
struct HasGradientSignature
{
const static bool value =
HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientBoolForm>::value ||
HasGradient<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType, Gradient>::template GradientVoidForm>::value;
};
//! Utility struct, check if either void GradientConstraint() or
//! bool GradientConstraint() exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType,
typename Gradient>
struct HasGradientConstraintSignature
{
const static bool value =
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
//! bool EndOptimization() exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
struct HasEndOptimizationSignature
{
const static bool value =
HasEndOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndOptimizationBoolForm>::value ||
HasEndOptimization<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndOptimizationVoidForm>::value;
};
//! Utility struct, check if either void BeginEpoch() or bool BeginEpoch()
//! exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
struct HasBeginEpochSignature
{
const static bool value =
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()
//! exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
struct HasEndEpochSignature
{
const 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 =
!HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochBoolForm>::value &&
HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochVoidForm>::value;
const static bool hasNone =
!HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochBoolForm>::value &&
!HasEndEpoch<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template EndEpochVoidForm>::value;
};
//! Utility struct, check if either void StepTaken() or bool StepTaken() exists.
template<typename CallbackType,
typename OptimizerType,
typename FunctionType,
typename MatType>
struct HasStepTakenSignature
{
const 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 =
!HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenBoolForm>::value &&
HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenVoidForm>::value;
const static bool hasNone =
!HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenBoolForm>::value &&
!HasStepTaken<CallbackType, TypedForms<OptimizerType,
FunctionType, MatType>::template StepTakenVoidForm>::value;
};
} // namespace traits
} // namespace callbacks
} // namespace ens
#endif
+10 -3
View File
@@ -82,13 +82,20 @@ class CMAES
* modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate);
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Get the step size.
size_t PopulationSize() const { return lambda; }
+99 -69
View File
@@ -41,13 +41,24 @@ CMAES<SelectionPolicyType>::CMAES(const size_t lambda,
//! Optimize the function (minimize).
template<typename SelectionPolicyType>
template<typename DecomposableFunctionType>
double CMAES<SelectionPolicyType>::Optimize(
DecomposableFunctionType& function, arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type CMAES<SelectionPolicyType>::Optimize(
SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
// Make sure that we have the methods that we need. Long name...
traits::CheckNonDifferentiableDecomposableFunctionTypeAPI<
DecomposableFunctionType>();
traits::CheckArbitrarySeparableFunctionTypeAPI<
SeparableFunctionType, BaseMatType>();
RequireDenseFloatingPointType<BaseMatType>();
BaseMatType& iterate = (BaseMatType&) iterateIn;
// Find the number of functions to use.
const size_t numFunctions = function.NumFunctions();
@@ -58,15 +69,15 @@ double CMAES<SelectionPolicyType>::Optimize(
// Parent weights.
const size_t mu = std::round(lambda / 2);
arma::vec w = std::log(mu + 0.5) - arma::log(
arma::linspace<arma::vec>(0, mu - 1, mu) + 1.0);
w /= arma::sum(w);
BaseMatType w = std::log(mu + 0.5) - arma::log(
arma::linspace<BaseMatType>(0, mu - 1, mu) + 1.0);
w /= arma::accu(w);
// Number of effective solutions.
const double muEffective = 1 / arma::accu(arma::pow(w, 2));
// Step size control parameters.
arma::vec sigma(3);
BaseMatType sigma(2, 1); // sigma is vector-shaped.
sigma(0) = 0.3 * (upperBound - lowerBound);
const double cs = (muEffective + 2) / (iterate.n_elem + muEffective + 5);
const double ds = 1 + cs + 2 * std::max(std::sqrt((muEffective - 1) /
@@ -86,143 +97,157 @@ double CMAES<SelectionPolicyType>::Optimize(
muEffective) / (std::pow(iterate.n_elem + 2, 2) +
alphaMu * muEffective / 2));
arma::cube mPosition(iterate.n_rows, iterate.n_cols, 3);
mPosition.slice(0) = lowerBound + arma::randu(
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);
arma::mat step = arma::zeros(iterate.n_rows, iterate.n_cols);
BaseMatType step(iterate.n_rows, iterate.n_cols);
step.zeros();
// Calculate the first objective function.
double currentObjective = 0;
ElemType currentObjective = 0;
for (size_t f = 0; f < numFunctions; f += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - f);
currentObjective += function.Evaluate(mPosition.slice(0), f,
const ElemType objective = function.Evaluate(mPosition[0], f,
effectiveBatchSize);
currentObjective += objective;
Callback::Evaluate(*this, function, mPosition[0], objective,
callbacks...);
}
double overallObjective = currentObjective;
double lastObjective = DBL_MAX;
ElemType overallObjective = currentObjective;
ElemType lastObjective = std::numeric_limits<ElemType>::max();
// Population parameters.
arma::cube pStep(iterate.n_rows, iterate.n_cols, lambda);
arma::cube pPosition(iterate.n_rows, iterate.n_cols, lambda);
arma::vec pObjective(lambda);
arma::cube ps = arma::zeros(iterate.n_rows, iterate.n_cols, 2);
arma::cube pc = ps;
arma::cube C(iterate.n_elem, iterate.n_elem, 2);
C.slice(0).eye();
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.
arma::vec eigval;
arma::mat eigvec;
arma::vec eigvalZero = arma::zeros(iterate.n_elem);
arma::Col<ElemType> 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);
// Controls early termination of the optimization process.
bool terminate = false;
// Now iterate!
for (size_t i = 1; i < maxIterations; ++i)
terminate |= Callback::BeginOptimization(*this, function, iterate,
callbacks...);
for (size_t i = 1; i < maxIterations && !terminate; ++i)
{
// To keep track of where we are.
const size_t idx0 = (i - 1) % 2;
const size_t idx1 = i % 2;
// Perform Cholesky decomposition. If the matrix is not positive definite,
// add a small value and try again
arma::mat covLower;
while (!arma::chol(covLower, C.slice(idx0), "lower"))
C.slice(idx0).diag() += 1e-16;
// add a small value and try again.
BaseMatType covLower;
while (!arma::chol(covLower, C[idx0], "lower"))
C[idx0].diag() += 1e-16;
for (size_t j = 0; j < lambda; ++j)
{
if (iterate.n_rows > iterate.n_cols)
{
pStep.slice(idx(j)) = covLower *
arma::randn(iterate.n_rows, iterate.n_cols);
pStep[idx(j)] = covLower *
arma::randn<BaseMatType>(iterate.n_rows, iterate.n_cols);
}
else
{
pStep.slice(idx(j)) = arma::randn(iterate.n_rows, iterate.n_cols) *
covLower;
pStep[idx(j)] = arma::randn<BaseMatType>(iterate.n_rows, iterate.n_cols)
* covLower;
}
pPosition.slice(idx(j)) = mPosition.slice(idx0) + sigma(idx0) *
pStep.slice(idx(j));
pPosition[idx(j)] = mPosition[idx0] + sigma(idx0) * pStep[idx(j)];
// Calculate the objective function.
pObjective(idx(j)) = selectionPolicy.Select(function, batchSize,
pPosition.slice(idx(j)));
pPosition[idx(j)], callbacks...);
}
// Sort population.
idx = sort_index(pObjective);
idx = arma::sort_index(pObjective);
step = w(0) * pStep.slice(idx(0));
step = w(0) * pStep[idx(0)];
for (size_t j = 1; j < mu; ++j)
step += w(j) * pStep.slice(idx(j));
step += w(j) * pStep[idx(j)];
mPosition.slice(idx1) = mPosition.slice(idx0) + sigma(idx0) * step;
mPosition[idx1] = mPosition[idx0] + sigma(idx0) * step;
// Calculate the objective function.
currentObjective = selectionPolicy.Select(function, batchSize,
mPosition.slice(idx1));
mPosition[idx1], callbacks...);
// Update best parameters.
if (currentObjective < overallObjective)
{
overallObjective = currentObjective;
iterate = mPosition.slice(idx1);
iterate = mPosition[idx1];
terminate |= Callback::StepTaken(*this, function, iterate, callbacks...);
}
// Update Step Size.
if (iterate.n_rows > iterate.n_cols)
{
ps.slice(idx1) = (1 - cs) * ps.slice(idx0) + std::sqrt(
ps[idx1] = (1 - cs) * ps[idx0] + std::sqrt(
cs * (2 - cs) * muEffective) * covLower.t() * step;
}
else
{
ps.slice(idx1) = (1 - cs) * ps.slice(idx0) + std::sqrt(
ps[idx1] = (1 - cs) * ps[idx0] + std::sqrt(
cs * (2 - cs) * muEffective) * step * covLower.t();
}
const double psNorm = arma::norm(ps.slice(idx1));
sigma(idx1) = sigma(idx0) * std::pow(
std::exp(cs / ds * psNorm / enn - 1), 0.3);
const ElemType psNorm = arma::norm(ps[idx1]);
sigma(idx1) = sigma(idx0) * std::exp(cs / ds * ( psNorm / enn - 1));
// Update covariance matrix.
if ((psNorm / sqrt(1 - std::pow(1 - cs, 2 * i))) < h)
{
pc.slice(idx1) = (1 - cc) * pc.slice(idx0) + std::sqrt(cc * (2 - cc) *
pc[idx1] = (1 - cc) * pc[idx0] + std::sqrt(cc * (2 - cc) *
muEffective) * step;
if (iterate.n_rows > iterate.n_cols)
{
C.slice(idx1) = (1 - c1 - cmu) * C.slice(idx0) + c1 *
(pc.slice(idx1) * pc.slice(idx1).t());
C[idx1] = (1 - c1 - cmu) * C[idx0] + c1 *
(pc[idx1] * pc[idx1].t());
}
else
{
C.slice(idx1) = (1 - c1 - cmu) * C.slice(idx0) + c1 *
(pc.slice(idx1).t() * pc.slice(idx1));
C[idx1] = (1 - c1 - cmu) * C[idx0] + c1 *
(pc[idx1].t() * pc[idx1]);
}
}
else
{
pc.slice(idx1) = (1 - cc) * pc.slice(idx0);
pc[idx1] = (1 - cc) * pc[idx0];
if (iterate.n_rows > iterate.n_cols)
{
C.slice(idx1) = (1 - c1 - cmu) * C.slice(idx0) + c1 * (pc.slice(idx1) *
pc.slice(idx1).t() + (cc * (2 - cc)) * C.slice(idx0));
C[idx1] = (1 - c1 - cmu) * C[idx0] + c1 * (pc[idx1] *
pc[idx1].t() + (cc * (2 - cc)) * C[idx0]);
}
else
{
C.slice(idx1) = (1 - c1 - cmu) * C.slice(idx0) + c1 *
(pc.slice(idx1).t() * pc.slice(idx1) + (cc * (2 - cc)) *
C.slice(idx0));
C[idx1] = (1 - c1 - cmu) * C[idx0] + c1 *
(pc[idx1].t() * pc[idx1] + (cc * (2 - cc)) * C[idx0]);
}
}
@@ -230,30 +255,30 @@ double CMAES<SelectionPolicyType>::Optimize(
{
for (size_t j = 0; j < mu; ++j)
{
C.slice(idx1) = C.slice(idx1) + cmu * w(j) *
pStep.slice(idx(j)) * pStep.slice(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.slice(idx1) = C.slice(idx1) + cmu * w(j) *
pStep.slice(idx(j)).t() * pStep.slice(idx(j));
C[idx1] = C[idx1] + cmu * w(j) *
pStep[idx(j)].t() * pStep[idx(j)];
}
}
arma::eig_sym(eigval, eigvec, C.slice(idx1));
const arma::uvec negativeEigval = find(eigval < 0, 1);
arma::eig_sym(eigval, eigvec, C[idx1]);
const arma::uvec negativeEigval = arma::find(eigval < 0, 1);
if (!negativeEigval.is_empty())
{
if (negativeEigval(0) == 0)
{
C.slice(idx1).zeros();
C[idx1].zeros();
}
else
{
C.slice(idx1) = eigvec.cols(0, negativeEigval(0) - 1) *
C[idx1] = eigvec.cols(0, negativeEigval(0) - 1) *
arma::diagmat(eigval.subvec(0, negativeEigval(0) - 1)) *
eigvec.cols(0, negativeEigval(0) - 1).t();
}
@@ -267,6 +292,8 @@ double CMAES<SelectionPolicyType>::Optimize(
{
Warn << "CMA-ES: converged to " << overallObjective << "; "
<< "terminating with failure. Try a smaller step size?" << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
@@ -274,12 +301,15 @@ double CMAES<SelectionPolicyType>::Optimize(
{
Info << "CMA-ES: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
lastObjective = overallObjective;
}
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
@@ -23,24 +23,29 @@ class FullSelection
/**
* Select the full dataset to calculate the objective function.
*
* @tparam DecomposableFunctionType Type of the function to be evaluated.
* @tparam SeparableFunctionType Type of the function to be evaluated.
* @param function Function to optimize.
* @param batchSize Batch size to use for each step.
* @param iterate starting point.
*/
template<typename DecomposableFunctionType>
double Select(DecomposableFunctionType& function,
const size_t batchSize,
const arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
double Select(SeparableFunctionType& function,
const size_t batchSize,
const MatType& iterate,
CallbackTypes&... callbacks)
{
// Find the number of functions to use.
const size_t numFunctions = function.NumFunctions();
double objective = 0;
typename MatType::elem_type objective = 0;
for (size_t f = 0; f < numFunctions; f += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - f);
objective += function.Evaluate(iterate, f, effectiveBatchSize);
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
}
return objective;
@@ -38,20 +38,23 @@ class RandomSelection
/**
* Randomly select dataset points to calculate the objective function.
*
* @tparam DecomposableFunctionType Type of the function to be evaluated.
* @tparam SeparableFunctionType Type of the function to be evaluated.
* @param function Function to optimize.
* @param batchSize Batch size to use for each step.
* @param iterate starting point.
*/
template<typename DecomposableFunctionType>
double Select(DecomposableFunctionType& function,
const size_t batchSize,
const arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename... CallbackTypes>
double Select(SeparableFunctionType& function,
const size_t batchSize,
const MatType& iterate,
CallbackTypes&... callbacks)
{
// Find the number of functions to use.
const size_t numFunctions = function.NumFunctions();
double objective = 0;
typename MatType::elem_type objective = 0;
for (size_t f = 0; f < std::floor(numFunctions * fraction); f += batchSize)
{
const size_t selection = arma::as_scalar(arma::randi<arma::uvec>(
@@ -60,6 +63,8 @@ class RandomSelection
numFunctions - selection);
objective += function.Evaluate(iterate, selection, effectiveBatchSize);
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
}
return objective;
+23 -17
View File
@@ -56,8 +56,10 @@ namespace ens {
* The whole process then repeats for multiple generation until at least one of
* the termination criteria is met:
*
* 1) The final value of the objective function (Not considered if not provided).
* 2) The maximum number of generation reached (optional but highly recommended).
* 1) The final value of the objective function (Not considered if not
* provided).
* 2) The maximum number of generation reached (optional but highly
* recommended).
* 3) Minimum change in best fitness values between two consecutive generations
* should be greater than a threshold value (Not considered if not provided).
*
@@ -100,13 +102,20 @@ class CNE
* starting point will be modified to store the finishing point of the
* algorithm, and the final objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @tparam ArbitraryFunctionType 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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate);
template<typename ArbitraryFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(ArbitraryFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Get the population size.
size_t PopulationSize() const { return populationSize; }
@@ -140,10 +149,14 @@ class CNE
private:
//! Reproduce candidates to create the next generation.
void Reproduce();
template<typename MatType>
void Reproduce(std::vector<MatType>& population,
const MatType& fitnessValues,
arma::uvec& index);
//! Modify weights with some noise for the evolution of next generation.
void Mutate();
template<typename MatType>
void Mutate(std::vector<MatType>& population, arma::uvec& index);
/**
* Crossover parents and create new childs. Two parents create two new childs.
@@ -157,20 +170,13 @@ class CNE
* generation and place a child over there for the
* next generation.
*/
void Crossover(const size_t mom,
template<typename MatType>
void Crossover(std::vector<MatType>& population,
const size_t mom,
const size_t dad,
const size_t dropout1,
const size_t dropout2);
//! Population matrix. Each column is a candidate.
arma::cube population;
//! Vector of fintness values corresponding to each candidate.
arma::vec fitnessValues;
//! Index of sorted fitness values.
arma::uvec index;
//! The number of candidates in the population.
size_t populationSize;
+75 -31
View File
@@ -37,9 +37,27 @@ inline CNE::CNE(const size_t populationSize,
{ /* Nothing to do here. */ }
//! Optimize the function.
template<typename DecomposableFunctionType>
double CNE::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
template<typename ArbitraryFunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type CNE::Optimize(ArbitraryFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
// Make sure that we have the methods that we need. Long name...
traits::CheckArbitraryFunctionTypeAPI<ArbitraryFunctionType,
BaseMatType>();
RequireDenseFloatingPointType<BaseMatType>();
// Vector of fitness values corresponding to each candidate.
BaseMatType fitnessValues;
//! Index of sorted fitness values.
arma::uvec index;
// Make sure for evolution to work at least four candidates are present.
if (populationSize < 4)
{
@@ -68,41 +86,58 @@ double CNE::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
"children. Increase population size.");
}
BaseMatType& iterate = (BaseMatType&) iterateIn;
// Generate the population based on a Gaussian distribution around the given
// starting point.
population = arma::randn(iterate.n_rows, iterate.n_cols, populationSize);
population.each_slice() += iterate;
std::vector<BaseMatType> population;
for (size_t i = 0 ; i < populationSize; ++i)
{
population.push_back(arma::randu<BaseMatType>(iterate.n_rows,
iterate.n_cols) + iterate);
}
// Store the number of elements in a cube slice or a matrix column.
elements = population.n_rows * population.n_cols;
// Store the number of elements in the objective matrix.
elements = iterate.n_rows * iterate.n_cols;
// initializing helper variables.
// Initialize helper variables.
fitnessValues.set_size(populationSize);
// Controls early termination of the optimization process.
bool terminate = false;
Info << "CNE initialized successfully. Optimization started."
<< std::endl;
// Find the fitness before optimization using given iterate parameters.
size_t lastBestFitness = function.Evaluate(iterate);
ElemType lastBestFitness = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, lastBestFitness, callbacks...);
// Iterate until maximum number of generations is obtained.
for (size_t gen = 1; gen <= maxGenerations; gen++)
terminate |= 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.slice(i);
iterate = population[i];
terminate |= Callback::StepTaken(*this, function, iterate,
callbacks...);
// Find fitness of candidate.
fitnessValues[i] = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, fitnessValues[i],
callbacks...);
}
Info << "Generation number: " << gen << " best fitness = "
<< fitnessValues.min() << std::endl;
// Create next generation of species.
Reproduce();
Reproduce(population, fitnessValues, index);
// Check for termination criteria.
if (std::abs(lastBestFitness - fitnessValues.min()) < tolerance)
@@ -117,13 +152,20 @@ double CNE::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
}
// Set the best candidate into the network parameters.
iterate = population.slice(index(0));
iterateIn = population[index(0)];
return function.Evaluate(iterate);
const ElemType objective = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, objective, callbacks...);
Callback::EndOptimization(*this, function, iterate, callbacks...);
return objective;
}
//! Reproduce candidates to create the next generation.
inline void CNE::Reproduce()
template<typename MatType>
inline void CNE::Reproduce(std::vector<MatType>& population,
const MatType& fitnessValues,
arma::uvec& index)
{
// Sort fitness values. Smaller fitness value means better performance.
index = arma::sort_index(fitnessValues);
@@ -158,54 +200,56 @@ inline void CNE::Reproduce()
// Parents generate 2 children replacing the dropped-out candidates.
// Also finding the index of these candidates in the population matrix.
Crossover(index[mom], index[dad], index[i], index[i + 1]);
Crossover(population, index[mom], index[dad], index[i], index[i + 1]);
}
// Mutating the weights with small noise values.
// This is done to bring change in the next generation.
Mutate();
Mutate(population, index);
}
//! Crossover parents to create new children.
inline void CNE::Crossover(const size_t mom,
template<typename MatType>
inline void CNE::Crossover(std::vector<MatType>& population,
const size_t mom,
const size_t dad,
const size_t child1,
const size_t child2)
{
// Replace the candidates with parents at their place.
population.slice(child1) = population.slice(mom);
population.slice(child2) = population.slice(dad);
// Preallocate random selection vector (values between 0 and 1).
arma::vec selection = arma::randu(elements);
population[child1] = population[mom];
population[child2] = population[dad];
// Randomly alter mom and dad genome weights to get two different children.
for (size_t i = 0; i < elements; i++)
{
// Using it to alter the weights of the children.
if (selection(i) > 0.5)
const double random = arma::randu<typename MatType::elem_type>();
if (random > 0.5)
{
population.slice(child1)(i) = population.slice(mom)(i);
population.slice(child2)(i) = population.slice(dad)(i);
population[child1](i) = population[mom](i);
population[child2](i) = population[dad](i);
}
else
{
population.slice(child1)(i) = population.slice(dad)(i);
population.slice(child2)(i) = population.slice(mom)(i);
population[child1](i) = population[dad](i);
population[child2](i) = population[mom](i);
}
}
}
//! Modify weights with some noise for the evolution of next generation.
inline void CNE::Mutate()
template<typename MatType>
inline void CNE::Mutate(std::vector<MatType>& population, arma::uvec& index)
{
// 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.slice(index(i)) += (arma::randu(
population.n_rows, population.n_cols) < mutationProb) %
(mutationSize * arma::randn(population.n_rows, population.n_cols));
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));
}
}
+21
View File
@@ -46,7 +46,28 @@
#if defined(ENS_USE_OPENMP)
#define ENS_PRAGMA_OMP_PARALLEL _Pragma("omp parallel")
#define ENS_PRAGMA_OMP_ATOMIC _Pragma("omp atomic")
#define ENS_PRAGMA_OMP_CRITICAL _Pragma("omp critical")
#define ENS_PRAGMA_OMP_CRITICAL_NAMED _Pragma("omp critical(section)")
#else
#define ENS_PRAGMA_OMP_PARALLEL
#define ENS_PRAGMA_OMP_ATOMIC
#define ENS_PRAGMA_OMP_CRITICAL
#define ENS_PRAGMA_OMP_CRITICAL_NAMED
#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
#endif
+12 -9
View File
@@ -43,12 +43,14 @@ namespace ens {
*
* For more information, see the following:
*
* @code
* @techreport{storn1995,
* title = {Differential Evolution—a simple and efficient adaptive scheme
* for global optimization over continuous spaces},
* author = {Storn, Rainer and Price, Kenneth},
* year = 1995
* }
* @endcode
*
* DE can optimize arbitrary functions. For more details, see the
* documentation on function types included with this distribution or on the
@@ -83,13 +85,20 @@ class DE
* starting point will be modified to store the finishing point of the
* algorithm, and the final objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @tparam FunctionType 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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate);
template<typename FunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(FunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks);
//! Get the population size.
size_t PopulationSize() const { return populationSize; }
@@ -117,12 +126,6 @@ class DE
double& Tolerance() { return tolerance; }
private:
//! Population matrix. Each column is a candidate.
arma::cube population;
//! Vector of fitness values corresponding to each candidate.
arma::vec fitnessValues;
//! The number of candidates in the population.
size_t populationSize;
+62 -27
View File
@@ -30,11 +30,31 @@ inline DE::DE(const size_t populationSize ,
{ /* Nothing to do here. */ }
//!Optimize the function
template<typename DecomposableFunctionType>
inline double DE::Optimize(DecomposableFunctionType& function,
arma::mat& iterate)
template<typename FunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type DE::Optimize(FunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
// Population Size must be atleast 3 for DE to work.
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
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;
// Make sure that we have the methods that we need. Long name...
traits::CheckArbitraryFunctionTypeAPI<
FunctionType, BaseMatType>();
RequireDenseFloatingPointType<BaseMatType>();
// 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"
@@ -43,53 +63,60 @@ inline double DE::Optimize(DecomposableFunctionType& function,
// Initialize helper variables.
fitnessValues.set_size(populationSize);
double lastBestFitness = DBL_MAX;
arma::mat bestElement;
ElemType lastBestFitness = DBL_MAX;
BaseMatType bestElement;
// Controls early termination of the optimization process.
bool terminate = false;
// Generate a population based on a Gaussian distribution around the given
// starting point. Also finds the best element of the population.
population = arma::randn(iterate.n_rows, iterate.n_cols, populationSize);
for (size_t i = 0; i < populationSize; i++)
{
population.slice(i) = population.slice(i) + iterate;
fitnessValues[i] = function.Evaluate(population.slice(i));
if(fitnessValues[i] < lastBestFitness)
population[i].randn(iterate.n_rows, iterate.n_cols);
population[i] += iterate;
fitnessValues[i] = function.Evaluate(population[i]);
Callback::Evaluate(*this, function, population[i], fitnessValues[i],
callbacks...);
if (fitnessValues[i] < lastBestFitness)
{
lastBestFitness = fitnessValues[i];
bestElement = population.slice(i);
bestElement = population[i];
}
}
// Iterate until maximum number of generations are completed.
for (size_t gen = 0; gen < maxGenerations; gen++)
terminate |= Callback::BeginOptimization(*this, function, iterate,
callbacks...);
for (size_t gen = 0; gen < maxGenerations && !terminate; gen++)
{
// Generate new population based on /best/1/bin strategy.
for (size_t member = 0; member < populationSize; member++)
{
iterate = population.slice(member);
iterate = population[member];
// Generate two different random numbers to choose two random members.
size_t l = 0, m = 0;
do
{
l = arma::as_scalar(arma::randi<arma::uvec>(
1, arma::distr_param(0, populationSize - 1)));
l = arma::randi<arma::uword>(arma::distr_param(0, populationSize - 1));
}
while(l == member);
while (l == member);
do
{
m = arma::as_scalar(arma::randi<arma::uvec>(
1, arma::distr_param(0, populationSize - 1)));
m = arma::randi<arma::uword>(arma::distr_param(0, populationSize - 1));
}
while(m == member && m == l);
while (m == member && m == l);
// Generate new "mutant" from two randomly chosen members.
arma::mat mutant = bestElement + differentialWeight *
(population.slice(l) - population.slice(m));
BaseMatType mutant = bestElement + differentialWeight *
(population[l] - population[m]);
// Perform crossover.
const arma::mat cr = arma::randu(iterate.n_rows);
const BaseMatType cr = arma::randu<BaseMatType>(iterate.n_rows);
for (size_t it = 0; it < iterate.n_rows; it++)
{
if (cr[it] >= crossoverRate)
@@ -98,25 +125,31 @@ inline double DE::Optimize(DecomposableFunctionType& function,
}
}
double iterateValue = function.Evaluate(iterate);
const double mutantValue = function.Evaluate(mutant);
ElemType iterateValue = function.Evaluate(iterate);
Callback::Evaluate(*this, function, iterate, iterateValue, callbacks...);
const ElemType mutantValue = function.Evaluate(mutant);
Callback::Evaluate(*this, function, mutant, mutantValue, callbacks...);
// Replace the current member if mutant is better.
if (mutantValue < iterateValue)
{
iterate = mutant;
iterateValue = mutantValue;
terminate |= Callback::StepTaken(*this, function, iterate,
callbacks...);
}
fitnessValues[member] = iterateValue;
population.slice(member) = iterate;
population[member] = iterate;
}
// Check for termination criteria.
if (std::abs(lastBestFitness - fitnessValues.min()) < tolerance)
{
Info << "DE: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
<< "terminating optimization." << std::endl;
break;
}
@@ -126,13 +159,15 @@ inline double DE::Optimize(DecomposableFunctionType& function,
{
if (fitnessValues[it] == lastBestFitness)
{
bestElement = population.slice(it);
bestElement = population[it];
break;
}
}
}
iterate = bestElement;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return lastBestFitness;
}
+3 -3
View File
@@ -12,16 +12,16 @@
// This follows the Semantic Versioning pattern defined in https://semver.org/.
#define ENS_VERSION_MAJOR 1
#define ENS_VERSION_MAJOR 2
// 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 14
#define ENS_VERSION_PATCH 4
#define ENS_VERSION_PATCH 2
// 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 "Difficult Crimp"
#define ENS_VERSION_NAME "No Direction Home"
namespace ens {
+44 -8
View File
@@ -31,8 +31,9 @@ namespace ens {
* year = {2016},
* url = {http://arxiv.org/abs/1611.01505}
* }
* @endcode
*
* For Eve to work, a DecomposableFunctionType template parameter is required.
* For Eve to work, a SeparableFunctionType template parameter is required.
* This class must implement the following function:
*
* size_t NumFunctions();
@@ -72,6 +73,8 @@ class Eve
* @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 exactObjective Calculate the exact objective (Default: estimate the
* final objective obtained on the last pass over the data).
*/
Eve(const double stepSize = 0.001,
const size_t batchSize = 32,
@@ -82,20 +85,45 @@ class Eve
const double clip = 10,
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true);
const bool shuffle = true,
const bool exactObjective = false);
/**
* Optimize the given function using stochastic gradient descent. The given
* starting point will be modified to store the finishing point of the
* algorithm, and the final objective value is returned.
* Optimize the given function using Eve. The given starting point will be
* modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @tparam SeparableFunctionType Type of the function to be optimized.
* @tparam MatType Type of the parameters matrix.
* @tparam GradType Type of the gradient matrix.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate);
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
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 stepSize; }
@@ -147,6 +175,11 @@ class Eve
//! Modify whether or not the individual functions are shuffled.
bool& Shuffle() { return shuffle; }
//! Get whether or not the actual objective is calculated.
bool ExactObjective() const { return exactObjective; }
//! Modify whether or not the actual objective is calculated.
bool& ExactObjective() { return exactObjective; }
private:
//! The step size for each example.
double stepSize;
@@ -178,6 +211,9 @@ class Eve
//! Controls whether or not the individual functions are shuffled when
//! iterating.
bool shuffle;
//! Controls whether or not the actual Objective value is calculated.
bool exactObjective;
};
} // namespace ens
+104 -54
View File
@@ -29,7 +29,8 @@ inline Eve::Eve(const double stepSize,
const double clip,
const size_t maxIterations,
const double tolerance,
const bool shuffle) :
const bool shuffle,
const bool exactObjective) :
stepSize(stepSize),
batchSize(batchSize),
beta1(beta1),
@@ -39,75 +40,71 @@ inline Eve::Eve(const double stepSize,
clip(clip),
maxIterations(maxIterations),
tolerance(tolerance),
shuffle(shuffle)
shuffle(shuffle),
exactObjective(exactObjective)
{ /* Nothing to do. */ }
//! Optimize the function (minimize).
template<typename DecomposableFunctionType>
double Eve::Optimize(
DecomposableFunctionType& function,
arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Eve::Optimize(SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
typedef Function<DecomposableFunctionType> FullFunctionType;
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
typedef Function<SeparableFunctionType, BaseMatType, BaseGradType>
FullFunctionType;
FullFunctionType& f(static_cast<FullFunctionType&>(function));
// Make sure we have all the methods that we need.
traits::CheckDecomposableFunctionTypeAPI<FullFunctionType>();
traits::CheckSeparableFunctionTypeAPI<FullFunctionType, BaseMatType,
BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
BaseMatType& iterate = (BaseMatType&) iterateIn;
// Find the number of functions to use.
const size_t numFunctions = f.NumFunctions();
// To keep track of where we are and how things are going.
size_t currentFunction = 0;
double overallObjective = 0;
double lastOverallObjective = DBL_MAX;
size_t epoch = 1;
ElemType overallObjective = 0;
ElemType lastOverallObjective = DBL_MAX;
double objective = 0;
double lastObjective = 0;
double dt = 1;
ElemType objective = 0;
ElemType lastObjective = 0;
ElemType dt = 1;
// Controls early termination of the optimization process.
bool terminate = false;
// The exponential moving average of gradient values.
arma::mat m = arma::zeros<arma::mat>(iterate.n_rows, iterate.n_cols);
BaseGradType m(iterate.n_rows, iterate.n_cols);
m.zeros();
// The exponential moving average of squared gradient values.
arma::mat v = arma::zeros<arma::mat>(iterate.n_rows, iterate.n_cols);
BaseGradType v(iterate.n_rows, iterate.n_cols);
v.zeros();
// Now iterate!
arma::mat gradient(iterate.n_rows, iterate.n_cols);
terminate |= 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;
for (size_t i = 0; i < actualMaxIterations; /* incrementing done manually */)
for (size_t i = 0; i < actualMaxIterations && !terminate;
/* incrementing done manually */)
{
// Is this iteration the start of a sequence?
if ((currentFunction % numFunctions) == 0 && i > 0)
{
// Output current objective function.
Info << "Eve: iteration " << i << ", objective " << overallObjective
<< "." << std::endl;
if (std::isnan(overallObjective) || std::isinf(overallObjective))
{
Warn << "Eve: converged to " << overallObjective << "; terminating"
<< " with failure. Try a smaller step size?" << std::endl;
return overallObjective;
}
if (std::abs(lastOverallObjective - overallObjective) < tolerance)
{
Info << "Eve: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
return overallObjective;
}
// Reset the counter variables.
lastOverallObjective = overallObjective;
overallObjective = 0;
currentFunction = 0;
if (shuffle) // Determine order of visitation.
f.Shuffle();
}
// Find the effective batch size; we have to take the minimum of three
// things:
// - the batch size can't be larger than the user-specified batch size;
@@ -124,6 +121,9 @@ double Eve::Optimize(
gradient, effectiveBatchSize);
overallObjective += objective;
terminate |= Callback::EvaluateWithGradient(*this, f, iterate,
objective, gradient, callbacks...);
m *= beta1;
m += (1 - beta1) * gradient;
@@ -135,11 +135,12 @@ double Eve::Optimize(
if (i > 0)
{
const double d = std::abs(objective - lastObjective) /
const ElemType d = std::abs(objective - lastObjective) /
(std::min(objective, lastObjective) + epsilon);
dt *= beta3;
dt += (1 - beta3) * std::min(std::max(d, 1.0 / clip), clip);
dt += (1 - beta3) * std::min(std::max(d, ElemType(1.0 / clip)),
ElemType(clip));
}
lastObjective = objective;
@@ -147,20 +148,69 @@ double Eve::Optimize(
iterate -= stepSize / dt * (m / biasCorrection1) /
(arma::sqrt(v / biasCorrection2) + epsilon);
terminate |= Callback::StepTaken(*this, f, iterate, callbacks...);
i += effectiveBatchSize;
currentFunction += effectiveBatchSize;
// Is this iteration the start of a sequence?
if ((currentFunction % numFunctions) == 0)
{
terminate |= Callback::EndEpoch(*this, f, iterate, epoch++,
overallObjective / (ElemType) numFunctions, callbacks...);
// Output current objective function.
Info << "Eve: iteration " << i << ", objective " << overallObjective
<< "." << std::endl;
if (std::isnan(overallObjective) || std::isinf(overallObjective))
{
Warn << "Eve: converged to " << overallObjective << "; terminating"
<< " with failure. Try a smaller step size?" << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
if (std::abs(lastOverallObjective - overallObjective) < tolerance ||
Callback::BeginEpoch(*this, f, iterate, epoch, overallObjective,
callbacks...))
{
Info << "Eve: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
// Reset the counter variables.
lastOverallObjective = overallObjective;
overallObjective = 0;
currentFunction = 0;
if (shuffle) // Determine order of visitation.
f.Shuffle();
}
}
Info << "Eve: maximum iterations (" << maxIterations << ") reached; "
<< "terminating optimization." << std::endl;
// Calculate final objective.
overallObjective = 0;
for (size_t i = 0; i < numFunctions; i += batchSize)
// Calculate final objective if exactObjective is set to true.
if (exactObjective)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - i);
overallObjective += f.Evaluate(iterate, i, effectiveBatchSize);
overallObjective = 0;
for (size_t i = 0; i < numFunctions; i += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - i);
const ElemType objective = f.Evaluate(iterate, i, effectiveBatchSize);
overallObjective += objective;
Callback::Evaluate(*this, f, iterate, objective, callbacks...);
}
}
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
+39 -5
View File
@@ -66,6 +66,8 @@ class FTML
* 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).
*/
FTML(const double stepSize = 0.001,
const size_t batchSize = 32,
@@ -75,22 +77,49 @@ class FTML
const size_t maxIterations = 100000,
const double tolerance = 1e-5,
const bool shuffle = true,
const bool resetPolicy = true);
const bool resetPolicy = true,
const bool exactObjective = false);
/**
* Optimize the given function using FTML. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
CallbackTypes&&... callbacks)
{
return optimizer.Optimize(function, iterate);
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.
@@ -133,6 +162,11 @@ class FTML
//! 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(); }
+4 -2
View File
@@ -25,7 +25,8 @@ inline FTML::FTML(const double stepSize,
const size_t maxIterations,
const double tolerance,
const bool shuffle,
const bool resetPolicy) :
const bool resetPolicy,
const bool exactObjective) :
optimizer(stepSize,
batchSize,
maxIterations,
@@ -33,7 +34,8 @@ inline FTML::FTML(const double stepSize,
shuffle,
FTMLUpdate(epsilon, beta1, beta2),
NoDecay(),
resetPolicy)
resetPolicy,
exactObjective)
{ /* Nothing to do. */ }
} // namespace ens
+80 -57
View File
@@ -34,7 +34,6 @@ namespace ens {
* }
* @endcode
*/
class FTMLUpdate
{
public:
@@ -44,7 +43,7 @@ class FTMLUpdate
* @param epsilon Epsilon is the minimum allowed gradient.
* @param beta1 Exponential decay rate for the first moment estimates.
* @param beta2 Exponential decay rate for the weighted infinity norm
estimates.
* estimates.
*/
FTMLUpdate(const double epsilon = 1e-8,
const double beta1 = 0.9,
@@ -55,51 +54,6 @@ class FTMLUpdate
iteration(0)
{ /* Do nothing. */ }
/**
* The Initialize method is called by SGD::Optimize method with UpdatePolicy
* FTMLUpdate before the start of the iteration update process.
*
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
void Initialize(const size_t rows, const size_t cols)
{
v = arma::zeros<arma::mat>(rows, cols);
z = arma::zeros<arma::mat>(rows, cols);
d = arma::zeros<arma::mat>(rows, cols);
}
/**
* Update step for FTML.
*
* @param iterate Parameter that minimizes the function.
* @param stepSize Step size to be used for the given iteration.
* @param gradient The gradient matrix.
*/
void Update(arma::mat& iterate,
const double stepSize,
const arma::mat& gradient)
{
// Increment the iteration counter variable.
++iteration;
// And update the iterate.
v *= beta2;
v += (1 - beta2) * (gradient % gradient);
const double biasCorrection1 = 1.0 - std::pow(beta1, iteration);
const double biasCorrection2 = 1.0 - std::pow(beta2, iteration);
arma::mat sigma = -beta1 * d;
d = biasCorrection1 / stepSize *
(arma::sqrt(v / biasCorrection2) + epsilon);
sigma += d;
z *= beta1;
z += (1 - beta1) * gradient - sigma % iterate;
iterate = -z / d;
}
//! 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.
@@ -115,6 +69,84 @@ class FTMLUpdate
//! 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
* instantiated at the start of the optimization, and holds parameters
* specific to an individual optimization.
*/
template<typename MatType, typename GradType>
class Policy
{
public:
/**
* This constructor is called by the SGD Optimize() method before the start
* of the iteration update process.
*
* @param parent AdamUpdate object.
* @param rows Number of rows in the gradient matrix.
* @param cols Number of columns in the gradient matrix.
*/
Policy(FTMLUpdate& parent, const size_t rows, const size_t cols) :
parent(parent)
{
v.zeros(rows, cols);
z.zeros(rows, cols);
d.zeros(rows, cols);
}
/**
* Update step for FTML.
*
* @param iterate Parameter that minimizes 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.
++parent.iteration;
// And update the iterate.
v *= parent.beta2;
v += (1 - parent.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);
MatType sigma = -parent.beta1 * d;
d = biasCorrection1 / stepSize *
(arma::sqrt(v / biasCorrection2) + parent.epsilon);
sigma += d;
z *= parent.beta1;
z += (1 - parent.beta1) * gradient - sigma % iterate;
iterate = -z / d;
}
private:
// Reference to instantiated parent object.
FTMLUpdate& parent;
// The exponential moving average of gradient values.
GradType v;
// The exponential moving average of squared gradient values.
GradType z;
// Parameter update term.
MatType d;
};
private:
// The epsilon value used to initialise the squared gradient parameter.
double epsilon;
@@ -125,17 +157,8 @@ class FTMLUpdate
// The second moment coefficient.
double beta2;
// The exponential moving average of gradient values.
arma::mat v;
// The exponential moving average of squared gradient values.
arma::mat z;
// Parmeter update term.
arma::mat d;
// The number of iterations.
double iteration;
size_t iteration;
};
} // namespace ens
+48 -43
View File
@@ -15,7 +15,7 @@
namespace ens {
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
class Function;
} // namespace ens
@@ -25,9 +25,9 @@ class Function;
#include "function/add_evaluate.hpp"
#include "function/add_gradient.hpp"
#include "function/add_evaluate_with_gradient.hpp"
#include "function/add_decomposable_evaluate.hpp"
#include "function/add_decomposable_gradient.hpp"
#include "function/add_decomposable_evaluate_with_gradient.hpp"
#include "function/add_separable_evaluate.hpp"
#include "function/add_separable_gradient.hpp"
#include "function/add_separable_evaluate_with_gradient.hpp"
namespace ens {
@@ -54,26 +54,28 @@ namespace ens {
* addition, this class does not (to the best of my knowledge) rely on any
* undefined behavior.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
class Function :
public AddDecomposableEvaluateWithGradientStatic<FunctionType>,
public AddDecomposableEvaluateWithGradientConst<FunctionType>,
public AddDecomposableEvaluateWithGradient<FunctionType>,
public AddDecomposableGradientStatic<FunctionType>,
public AddDecomposableGradientConst<FunctionType>,
public AddDecomposableGradient<FunctionType>,
public AddDecomposableEvaluateStatic<FunctionType>,
public AddDecomposableEvaluateConst<FunctionType>,
public AddDecomposableEvaluate<FunctionType>,
public AddEvaluateWithGradientStatic<FunctionType>,
public AddEvaluateWithGradientConst<FunctionType>,
public AddEvaluateWithGradient<FunctionType>,
public AddGradientStatic<FunctionType>,
public AddGradientConst<FunctionType>,
public AddGradient<FunctionType>,
public AddEvaluateStatic<FunctionType>,
public AddEvaluateConst<FunctionType>,
public AddEvaluate<FunctionType>,
public AddSeparableEvaluateWithGradientStatic<FunctionType, MatType,
GradType>,
public AddSeparableEvaluateWithGradientConst<FunctionType, MatType,
GradType>,
public AddSeparableEvaluateWithGradient<FunctionType, MatType, GradType>,
public AddSeparableGradientStatic<FunctionType, MatType, GradType>,
public AddSeparableGradientConst<FunctionType, MatType, GradType>,
public AddSeparableGradient<FunctionType, MatType, GradType>,
public AddSeparableEvaluateStatic<FunctionType, MatType, GradType>,
public AddSeparableEvaluateConst<FunctionType, MatType, GradType>,
public AddSeparableEvaluate<FunctionType, MatType, GradType>,
public AddEvaluateWithGradientStatic<FunctionType, MatType, GradType>,
public AddEvaluateWithGradientConst<FunctionType, MatType, GradType>,
public AddEvaluateWithGradient<FunctionType, MatType, GradType>,
public AddGradientStatic<FunctionType, MatType, GradType>,
public AddGradientConst<FunctionType, MatType, GradType>,
public AddGradient<FunctionType, MatType, GradType>,
public AddEvaluateStatic<FunctionType, MatType, GradType>,
public AddEvaluateConst<FunctionType, MatType, GradType>,
public AddEvaluate<FunctionType, MatType, GradType>,
public FunctionType
{
public:
@@ -81,26 +83,29 @@ class Function :
// an unconstructable overload with the same name, so we can use using
// declarations here to ensure that they are all accessible. Since we don't
// know what FunctionType has, we can't use any using declarations there.
using AddDecomposableEvaluateWithGradientStatic<
FunctionType>::EvaluateWithGradient;
using AddDecomposableEvaluateWithGradientConst<
FunctionType>::EvaluateWithGradient;
using AddDecomposableEvaluateWithGradient<FunctionType>::EvaluateWithGradient;
using AddDecomposableGradientStatic<FunctionType>::Gradient;
using AddDecomposableGradientConst<FunctionType>::Gradient;
using AddDecomposableGradient<FunctionType>::Gradient;
using AddDecomposableEvaluateStatic<FunctionType>::Evaluate;
using AddDecomposableEvaluateConst<FunctionType>::Evaluate;
using AddDecomposableEvaluate<FunctionType>::Evaluate;
using AddEvaluateWithGradientStatic<FunctionType>::EvaluateWithGradient;
using AddEvaluateWithGradientConst<FunctionType>::EvaluateWithGradient;
using AddEvaluateWithGradient<FunctionType>::EvaluateWithGradient;
using AddGradientStatic<FunctionType>::Gradient;
using AddGradientConst<FunctionType>::Gradient;
using AddGradient<FunctionType>::Gradient;
using AddEvaluateStatic<FunctionType>::Evaluate;
using AddEvaluateConst<FunctionType>::Evaluate;
using AddEvaluate<FunctionType>::Evaluate;
using AddSeparableEvaluateWithGradientStatic<
FunctionType, MatType, GradType>::EvaluateWithGradient;
using AddSeparableEvaluateWithGradientConst<
FunctionType, MatType, GradType>::EvaluateWithGradient;
using AddSeparableEvaluateWithGradient<
FunctionType, MatType, GradType>::EvaluateWithGradient;
using AddSeparableGradientStatic<
FunctionType, MatType, GradType>::Gradient;
using AddSeparableGradientConst<FunctionType, MatType, GradType>::Gradient;
using AddSeparableGradient<FunctionType, MatType, GradType>::Gradient;
using AddSeparableEvaluateStatic<
FunctionType, MatType, GradType>::Evaluate;
using AddSeparableEvaluateConst<FunctionType, MatType, GradType>::Evaluate;
using AddSeparableEvaluate<FunctionType, MatType, GradType>::Evaluate;
using AddEvaluateWithGradientStatic<FunctionType, MatType, GradType>::EvaluateWithGradient;
using AddEvaluateWithGradientConst<FunctionType, MatType, GradType>::EvaluateWithGradient;
using AddEvaluateWithGradient<FunctionType, MatType, GradType>::EvaluateWithGradient;
using AddGradientStatic<FunctionType, MatType, GradType>::Gradient;
using AddGradientConst<FunctionType, MatType, GradType>::Gradient;
using AddGradient<FunctionType, MatType, GradType>::Gradient;
using AddEvaluateStatic<FunctionType, MatType, GradType>::Evaluate;
using AddEvaluateConst<FunctionType, MatType, GradType>::Evaluate;
using AddEvaluate<FunctionType, MatType, GradType>::Evaluate;
};
} // namespace ens
@@ -1,214 +0,0 @@
/**
* @file add_decomposable_evaluate.hpp
* @author Ryan Curtin
*
* Adds a decomposable Evaluate() function if a decomposable
* EvaluateWithGradient() function exists.
*
* 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_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_HPP
#define ENSMALLEN_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_HPP
#include "traits.hpp"
namespace ens {
/**
* The AddDecomposableEvaluate mixin class will add a decomposable Evaluate()
* method if a decomposable EvaluateWithGradient() function exists, or nothing
* otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientForm>::value,
bool HasDecomposableEvaluate =
traits::HasEvaluate<FunctionType,
traits::DecomposableEvaluateForm>::value>
class AddDecomposableEvaluate
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
double Evaluate(traits::UnconstructableType&, const size_t, const size_t);
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType, bool HasDecomposableEvaluateWithGradient>
class AddDecomposableEvaluate<FunctionType, HasDecomposableEvaluateWithGradient,
true>
{
public:
// Reflect the existing Evaluate().
double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize)
{
return static_cast<FunctionType*>(
static_cast<Function<FunctionType>*>(this))->Evaluate(coordinates,
begin, batchSize);
}
};
/**
* If we have a decomposable EvaluateWithGradient() but not a decomposable
* Evaluate(), add a decomposable Evaluate() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluate<FunctionType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given decomposable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize)
{
arma::mat gradient; // This will be ignored.
return static_cast<Function<FunctionType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddDecomposableEvaluateConst mixin class will add a decomposable const
* Evaluate() method if a decomposable const EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientConstForm>::value,
bool HasDecomposableEvaluate =
traits::HasEvaluate<FunctionType,
traits::DecomposableEvaluateConstForm>::value>
class AddDecomposableEvaluateConst
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
double Evaluate(traits::UnconstructableType&, const size_t, const size_t)
const;
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType, bool HasDecomposableEvaluateWithGradient>
class AddDecomposableEvaluateConst<FunctionType,
HasDecomposableEvaluateWithGradient, true>
{
public:
// Reflect the existing Evaluate().
double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize) const
{
return static_cast<const FunctionType*>(
static_cast<const Function<FunctionType>*>(this))->Evaluate(coordinates,
begin, batchSize);
}
};
/**
* If we have a decomposable const EvaluateWithGradient() but not a decomposable
* const Evaluate(), add a decomposable const Evaluate() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluateConst<FunctionType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given decomposable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize) const
{
arma::mat gradient; // This will be ignored.
return
static_cast<const Function<FunctionType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddDecomposableEvaluateStatic mixin class will add a decomposable static
* Evaluate() method if a decomposable static EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientStaticForm>::value,
bool HasDecomposableEvaluate =
traits::HasEvaluate<FunctionType,
traits::DecomposableEvaluateStaticForm>::value>
class AddDecomposableEvaluateStatic
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
double Evaluate(traits::UnconstructableType&, const size_t) const;
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType, bool HasDecomposableEvaluateWithGradient>
class AddDecomposableEvaluateStatic<FunctionType,
HasDecomposableEvaluateWithGradient, true>
{
public:
// Reflect the existing Evaluate().
static double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize)
{
return FunctionType::Evaluate(coordinates, begin, batchSize);
}
};
/**
* If we have a decomposable EvaluateWithGradient() but not a decomposable
* Evaluate(), add a decomposable Evaluate() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluateStatic<FunctionType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given decomposable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
static double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize)
{
arma::mat gradient; // This will be ignored.
return FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
} // namespace ens
#endif
@@ -1,165 +0,0 @@
/**
* @file add_decomposable_evaluate.hpp
* @author Ryan Curtin
*
* Adds a decomposable Evaluate() function if a decomposable
* EvaluateWithGradient() function exists.
*
* 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_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_HPP
#define ENSMALLEN_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_HPP
#include "traits.hpp"
namespace ens {
/**
* The AddDecomposableEvaluate mixin class will add a decomposable Evaluate()
* method if a decomposable EvaluateWithGradient() function exists, or nothing
* otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientForm>::value,
bool HasDecomposableEvaluate =
traits::HasEvaluate<FunctionType,
traits::DecomposableEvaluateForm>::value>
class AddDecomposableEvaluate
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
double Evaluate(traits::UnconstructableType&, const size_t);
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType, bool HasDecomposableEvaluateWithGradient>
class AddDecomposableEvaluate<FunctionType, HasDecomposableEvaluateWithGradient,
true>
{
public:
// Reflect the existing Evaluate().
double Evaluate(const arma::mat& coordinates, const size_t index)
{
return static_cast<FunctionType*>(
static_cast<Function<FunctionType>*>(this))->Evaluate(coordinates,
index);
}
};
/**
* If we have a decomposable EvaluateWithGradient() but not a decomposable
* Evaluate(), add a decomposable Evaluate() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluate<FunctionType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given decomposable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize)
{
arma::mat gradient; // This will be ignored.
return static_cast<Function<FunctionType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddDecomposableEvaluateConst mixin class will add a decomposable const
* Evaluate() method if a decomposable const EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientConstForm>::value,
bool HasDecomposableEvaluate =
traits::HasEvaluate<FunctionType,
traits::DecomposableEvaluateConstForm>::value>
class AddDecomposableEvaluateConst { };
/**
* If we have a decomposable const EvaluateWithGradient() but not a decomposable
* const Evaluate(), add a decomposable const Evaluate() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluateConst<FunctionType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given decomposable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize) const
{
arma::mat gradient; // This will be ignored.
return
static_cast<const Function<FunctionType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddDecomposableEvaluateStatic mixin class will add a decomposable static
* Evaluate() method if a decomposable static EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientStaticForm>::value,
bool HasDecomposableEvaluate =
traits::HasEvaluate<FunctionType,
traits::DecomposableEvaluateStaticForm>::value>
class AddDecomposableEvaluateStatic { };
/**
* If we have a decomposable EvaluateWithGradient() but not a decomposable
* Evaluate(), add a decomposable Evaluate() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluateStatic<FunctionType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given decomposable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
static double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize)
{
arma::mat gradient; // This will be ignored.
return FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
} // namespace ens
#endif
@@ -1,259 +0,0 @@
/**
* @file add_decomposable_evaluate_with_gradient.hpp
* @author Ryan Curtin
*
* Adds a decomposable EvaluateWithGradient() function if both a decomposable
* Evaluate() and a decomposable Gradient() function exist.
*
* 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_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_W_GRADIENT_HPP
#define ENSMALLEN_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_W_GRADIENT_HPP
#include "traits.hpp"
namespace ens {
/**
* The AddDecomposableEvaluateWithGradient mixin class will add a decomposable
* EvaluateWithGradient() method if a decomposable Evaluate() method and a
* decomposable Gradient() method exists, or nothing otherwise.
*/
template<typename FunctionType,
// Check if there is at least one non-const Evaluate() or Gradient().
bool HasDecomposableEvaluateGradient = traits::HasNonConstSignatures<
FunctionType,
traits::HasEvaluate,
traits::DecomposableEvaluateForm,
traits::DecomposableEvaluateConstForm,
traits::DecomposableEvaluateStaticForm,
traits::HasGradient,
traits::DecomposableGradientForm,
traits::DecomposableGradientConstForm,
traits::DecomposableGradientStaticForm>::value,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientForm>::value>
class AddDecomposableEvaluateWithGradient
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
double EvaluateWithGradient(traits::UnconstructableType&, const size_t,
const size_t);
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType, bool HasDecomposableEvaluateGradient>
class AddDecomposableEvaluateWithGradient<FunctionType,
HasDecomposableEvaluateGradient, true>
{
public:
// Reflect the existing Evaluate().
double EvaluateWithGradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
return static_cast<FunctionType*>(
static_cast<Function<FunctionType>*>(this))->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* If we have a both decomposable Evaluate() and a decomposable Gradient() but
* not a decomposable EvaluateWithGradient(), add a decomposable
* EvaluateWithGradient() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluateWithGradient<FunctionType, true, false>
{
public:
/**
* Return both the evaluated objective function and its gradient, storing the
* gradient in the given matrix, starting at the given decomposable function
* and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of decomposable function to begin with.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of decomposable functions to evaluate.
*/
double EvaluateWithGradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
const double objective =
static_cast<Function<FunctionType>*>(this)->Evaluate(coordinates, begin,
batchSize);
static_cast<Function<FunctionType>*>(this)->Gradient(coordinates, begin,
gradient, batchSize);
return objective;
}
};
/**
* The AddDecomposableEvaluateWithGradientConst mixin class will add a
* decomposable const EvaluateWithGradient() method if both a decomposable const
* Evaluate() and a decomposable const Gradient() function exist, or nothing
* otherwise.
*/
template<typename FunctionType,
// Check if there is at least one const Evaluate() or Gradient().
bool HasDecomposableEvaluateGradient = traits::HasConstSignatures<
FunctionType,
traits::HasEvaluate,
traits::DecomposableEvaluateConstForm,
traits::DecomposableEvaluateStaticForm,
traits::HasGradient,
traits::DecomposableGradientConstForm,
traits::DecomposableGradientStaticForm>::value,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientConstForm>::value>
class AddDecomposableEvaluateWithGradientConst
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
double EvaluateWithGradient(traits::UnconstructableType&, const size_t,
const size_t) const;
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType, bool HasDecomposableEvaluateGradient>
class AddDecomposableEvaluateWithGradientConst<FunctionType,
HasDecomposableEvaluateGradient, true>
{
public:
// Reflect the existing Evaluate().
double EvaluateWithGradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize) const
{
return static_cast<const FunctionType*>(
static_cast<const Function<FunctionType>*>(this))->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* If we have both a decomposable const Evaluate() and a decomposable const
* Gradient() but not a decomposable const EvaluateWithGradient(), add a
* decomposable const EvaluateWithGradient() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluateWithGradientConst<FunctionType, true, false>
{
public:
/**
* Return both the evaluated objective function and its gradient, storing the
* gradient in the given matrix, starting at the given decomposable function
* and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of decomposable function to begin with.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of decomposable functions to evaluate.
*/
double EvaluateWithGradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize) const
{
const double objective =
static_cast<const Function<FunctionType>*>(this)->Evaluate(coordinates,
begin, batchSize);
static_cast<const Function<FunctionType>*>(this)->Gradient(coordinates,
begin, gradient, batchSize);
return objective;
}
};
/**
* The AddDecomposableEvaluateWithGradientStatic mixin class will add a
* decomposable static EvaluateWithGradient() method if both a decomposable
* static Evaluate() and a decomposable static gradient() function exist, or
* nothing otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateGradient =
traits::HasEvaluate<FunctionType,
traits::DecomposableEvaluateStaticForm>::value &&
traits::HasGradient<FunctionType,
traits::DecomposableGradientStaticForm>::value,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientStaticForm>::value>
class AddDecomposableEvaluateWithGradientStatic
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
static double EvaluateWithGradient(traits::UnconstructableType&, const size_t,
const size_t);
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType, bool HasDecomposableEvaluateGradient>
class AddDecomposableEvaluateWithGradientStatic<FunctionType,
HasDecomposableEvaluateGradient, true>
{
public:
// Reflect the existing Evaluate().
static double EvaluateWithGradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
return FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
/**
* If we have a decomposable static Evaluate() and a decomposable static
* Gradient() but not a decomposable static EvaluateWithGradient(), add a
* decomposable static Gradient() method.
*/
template<typename FunctionType>
class AddDecomposableEvaluateWithGradientStatic<FunctionType, true, false>
{
public:
/**
* Return both the evaluated objective function and its gradient, storing the
* gradient in the given matrix, starting at the given decomposable function
* and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of decomposable function to begin with.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of decomposable functions to evaluate.
*/
double EvaluateWithGradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize) const
{
const double objective = FunctionType::Evaluate(coordinates, begin,
batchSize);
FunctionType::Gradient(coordinates, begin, gradient, batchSize);
return objective;
}
};
} // namespace ens
#endif
@@ -1,185 +0,0 @@
/**
* @file add_decomposable_evaluate_with_gradient.hpp
* @author Ryan Curtin
*
* Add decomposable variants of Evaluate(), Gradient(), and
* EvaluateWithGradient().
*
* 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_ADD_DECOMPOSABLE_EVALUATE_WITH_GRADIENT_CONST_HPP
#define ENSMALLEN_ADD_DECOMPOSABLE_EVALUATE_WITH_GRADIENT_CONST_HPP
#include "traits.hpp"
namespace ens {
namespace aux {
template<typename FunctionType>
using DecomposableEvaluateForm = double(FunctionType::*)(const arma::mat&,
const size_t,
const size_t);
template<typename FunctionType>
using DecomposableEvaluateConstForm =
double(FunctionType::*)(const arma::mat&, const size_t, const size_t) const;
template<typename FunctionType>
using DecomposableEvaluateStaticForm = double(*)(const arma::mat&,
size_t,
size_t);
template<typename FunctionType>
using DecomposableGradientForm = void(FunctionType::*)(const arma::mat&,
const size_t,
arma::mat&,
const size_t);
template<typename FunctionType>
using DecomposableGradientConstForm =
void(FunctionType::*)(const arma::mat&,
const size_t,
arma::mat&,
const size_t) const;
template<typename FunctionType, typename... Ts>
using DecomposableGradientStaticForm = void(*)(const arma::mat&,
const size_t,
arma::mat&);
template<typename FunctionType>
using DecomposableEvaluateWithGradientForm =
double(FunctionType::*)(const arma::mat&,
const size_t,
arma::mat&,
const size_t);
template<typename FunctionType>
using DecomposableEvaluateWithGradientConstForm =
void(FunctionType::*)(const arma::mat&,
const size_t,
arma::mat&,
const size_t) const;
template<typename FunctionType>
using DecomposableEvaluateWithGradientStaticForm =
double(*)(const arma::mat&, const size_t, arma::mat&, const size_t);
} // namespace aux
/**
* The AddDecomposableEvaluateWithGradient mixin class will provide a
* decomposable Evaluate() and Gradient() method if the given class has a
* decomposable EvaluateWithGradient() method, or it will provide a decomposable
* EvaluateWithGradient() method if the class has a decomposable Evaluate() and
* Gradient() method, or it will provide nothing in any other case.
*/
template<typename FunctionType,
bool HasDecomposableEvaluate =
aux::HasEvaluate<FunctionType,
aux::DecomposableEvaluateForm>::value ||
aux::HasEvaluate<FunctionType,
aux::DecomposableEvaluateConstForm>::value ||
aux::HasEvaluate<FunctionType,
aux::DecomposableEvaluateStaticForm>::value,
bool HasGradient =
aux::HasGradient<FunctionType,
aux::DecomposableGradientForm>::value ||
aux::HasGradient<FunctionType,
aux::DecomposableGradientConstForm>::value ||
aux::HasGradient<FunctionType,
aux::DecomposableGradientStaticForm>::value,
bool HasEvaluateWithGradient =
aux::HasEvaluateWithGradient<FunctionType,
aux::DecomposableEvaluateWithGradientForm>::value ||
aux::HasEvaluateWithGradient<FunctionType,
aux::DecomposableEvaluateWithGradientConstForm>::value ||
aux::HasEvaluateWithGradient<FunctionType,
aux::DecomposableEvaluateWithGradientStaticForm>::value>
class AddDecomposableEvaluateWithGradient : public FunctionType { };
/**
* If the FunctionType has Evaluate() and Gradient() but not
* EvaluateWithGradient(), we will provide the latter.
*/
template<typename FunctionType>
class AddDecomposableEvaluateWithGradient<FunctionType, true, true, false> :
public FunctionType
{
public:
/**
* Return both the evaluated objective function and its gradient, storing the
* gradient in the given matrix.
*
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
double EvaluateWithGradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
const double objective = FunctionType::Evaluate(coordinates, begin,
batchSize);
FunctionType::Gradient(coordinates, begin, gradient, batchSize);
return objective;
}
};
/**
* If the FunctionType has EvaluateWithGradient() but not Evaluate(), provide
* that function.
*/
template<typename FunctionType>
class AddDecomposableEvaluateWithGradient<FunctionType, false, true, true> :
public FunctionType
{
public:
/**
* Return the objective function for the given coordinates.
*
* @param coordinates Coordinates to evaluate the function at.
*/
double Evaluate(const arma::mat& coordinates,
const size_t begin,
const size_t batchSize)
{
arma::mat gradient; // This will be ignored.
return FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
/**
* If the FunctionType has EvaluateWithGradient() but not Gradient(), provide
* that function.
*/
template<typename FunctionType>
class AddDecomposableEvaluateWithGradient<FunctionType, true, false, true> :
public FunctionType
{
public:
/**
* Calculate the gradient and store it in the given matrix.
*
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
void Gradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
// The returned objective value will be ignored.
(void) FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
} // namespace ens
#endif
@@ -1,224 +0,0 @@
/**
* @file add_decomposable_gradient.hpp
* @author Ryan Curtin
*
* Adds a decomposable Gradient() function if a decomposable
* EvaluateWithGradient() function exists.
*
* 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_FUNCTION_ADD_DECOMPOSABLE_GRADIENT_HPP
#define ENSMALLEN_FUNCTION_ADD_DECOMPOSABLE_GRADIENT_HPP
#include "traits.hpp"
namespace ens {
/**
* The AddDecomposableGradient mixin class will add a decomposable Gradient()
* method if a decomposable EvaluateWithGradient() function exists, or nothing
* otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientForm>::value,
bool HasDecomposableGradient =
traits::HasGradient<FunctionType,
traits::DecomposableGradientForm>::value>
class AddDecomposableGradient
{
public:
// Provide a dummy overload so the name 'Gradient' exists for this object.
void Gradient(traits::UnconstructableType&, const size_t, const size_t);
};
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType, bool HasDecomposableEvaluateWithGradient>
class AddDecomposableGradient<FunctionType, HasDecomposableEvaluateWithGradient,
true>
{
public:
// Reflect the existing Gradient().
void Gradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
static_cast<FunctionType*>(
static_cast<Function<FunctionType>*>(this))->Gradient(coordinates,
begin, gradient, batchSize);
}
};
/**
* If we have a decomposable EvaluateWithGradient() but not a decomposable
* Gradient(), add a decomposable Evaluate() method.
*/
template<typename FunctionType>
class AddDecomposableGradient<FunctionType, true, false>
{
public:
/**
* Calculate the gradient and store it in the given matrix, starting at the
* given decomposable function index and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of decomposable function to start at.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of decomposable functions to calculate for.
*/
void Gradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
// The returned objective value will be ignored.
(void) static_cast<Function<FunctionType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddDecomposableGradientConst mixin class will add a decomposable const
* Gradient() method if a decomposable const EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientConstForm>::value,
bool HasDecomposableGradient =
traits::HasGradient<FunctionType,
traits::DecomposableGradientConstForm>::value>
class AddDecomposableGradientConst
{
public:
// Provide a dummy overload so the name 'Gradient' exists for this object.
void Gradient(traits::UnconstructableType&, const size_t, const size_t) const;
};
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType, bool HasDecomposableEvaluateWithGradient>
class AddDecomposableGradientConst<FunctionType,
HasDecomposableEvaluateWithGradient, true>
{
public:
// Reflect the existing Gradient().
void Gradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize) const
{
static_cast<const FunctionType*>(
static_cast<const Function<FunctionType>*>(this))->Gradient(coordinates,
begin, gradient, batchSize);
}
};
/**
* If we have a decomposable const EvaluateWithGradient() but not a decomposable
* const Gradient(), add a decomposable const Gradient() method.
*/
template<typename FunctionType>
class AddDecomposableGradientConst<FunctionType, true, false>
{
public:
/**
* Calculate the gradient and store it in the given matrix, starting at the
* given decomposable function index and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of decomposable function to start at.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of decomposable functions to calculate for.
*/
void Gradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize) const
{
// The returned objective value will be ignored.
(void) static_cast<
const Function<FunctionType>*>(this)->EvaluateWithGradient(coordinates,
begin, gradient, batchSize);
}
};
/**
* The AddDecomposableEvaluateStatic mixin class will add a decomposable static
* Gradient() method if a decomposable static EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
bool HasDecomposableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::DecomposableEvaluateWithGradientStaticForm>::value,
bool HasDecomposableGradient =
traits::HasGradient<FunctionType,
traits::DecomposableGradientStaticForm>::value>
class AddDecomposableGradientStatic
{
public:
// Provide a dummy overload so the name 'Gradient' exists for this object.
static void Gradient(traits::UnconstructableType&,
const size_t,
const size_t);
};
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType, bool HasDecomposableEvaluateWithGradient>
class AddDecomposableGradientStatic<FunctionType,
HasDecomposableEvaluateWithGradient, true>
{
public:
// Reflect the existing Gradient().
static void Gradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
FunctionType::Gradient(coordinates, begin, gradient, batchSize);
}
};
/**
* If we have a decomposable EvaluateWithGradient() but not a decomposable
* Gradient(), add a decomposable Gradient() method.
*/
template<typename FunctionType>
class AddDecomposableGradientStatic<FunctionType, true, false>
{
public:
/**
* Calculate the gradient and store it in the given matrix, starting at the
* given decomposable function index and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of decomposable function to start at.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of decomposable functions to calculate for.
*/
static void Gradient(const arma::mat& coordinates,
const size_t begin,
arma::mat& gradient,
const size_t batchSize)
{
// The returned objective value will be ignored.
(void) FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
} // namespace ens
#endif
@@ -22,30 +22,44 @@ namespace ens {
* FunctionType has EvaluateWithGradient(), or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::EvaluateWithGradientForm>::value,
traits::TypedForms<MatType, GradType>::template
EvaluateWithGradientForm
>::value,
bool HasEvaluate =
traits::HasEvaluate<FunctionType, traits::EvaluateForm>::value>
traits::HasEvaluate<FunctionType,
traits::TypedForms<MatType, GradType>::template
EvaluateForm>::value>
class AddEvaluate
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
double Evaluate(traits::UnconstructableType&);
typename MatType::elem_type Evaluate(traits::UnconstructableType&);
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType, bool HasEvaluateWithGradient>
class AddEvaluate<FunctionType, HasEvaluateWithGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient>
class AddEvaluate<FunctionType,
MatType,
GradType,
HasEvaluateWithGradient,
true>
{
public:
// Reflect the existing Evaluate().
double Evaluate(const arma::mat& coordinates)
typename MatType::elem_type Evaluate(const MatType& coordinates)
{
return static_cast<FunctionType*>(static_cast<Function<FunctionType>*>(
this))->Evaluate(coordinates);
return static_cast<FunctionType*>(
static_cast<Function<FunctionType,
MatType, GradType>*>(this))->Evaluate(coordinates);
}
};
@@ -53,8 +67,8 @@ class AddEvaluate<FunctionType, HasEvaluateWithGradient, true>
* If we have EvaluateWithGradient() but no existing Evaluate(), add an
* Evaluate() method.
*/
template<typename FunctionType>
class AddEvaluate<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddEvaluate<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -62,10 +76,12 @@ class AddEvaluate<FunctionType, true, false>
*
* @param coordinates Coordinates to evaluate the function at.
*/
double Evaluate(const arma::mat& coordinates)
typename MatType::elem_type Evaluate(const MatType& coordinates)
{
arma::mat gradient; // This will be ignored.
return static_cast<Function<FunctionType>*>(this)->EvaluateWithGradient(
GradType gradient; // This will be ignored.
return static_cast<Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(
coordinates, gradient);
}
};
@@ -76,32 +92,47 @@ class AddEvaluate<FunctionType, true, false>
* otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::EvaluateWithGradientConstForm>::value,
traits::TypedForms<MatType,
GradType>::template
EvaluateWithGradientConstForm
>::value,
bool HasEvaluate =
traits::HasEvaluate<FunctionType,
traits::EvaluateConstForm>::value>
traits::TypedForms<MatType, GradType>::template
EvaluateConstForm
>::value>
class AddEvaluateConst
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
double Evaluate(traits::UnconstructableType&) const;
typename MatType::elem_type Evaluate(traits::UnconstructableType&) const;
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType, bool HasEvaluateWithGradient>
class AddEvaluateConst<FunctionType, HasEvaluateWithGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient>
class AddEvaluateConst<FunctionType,
MatType,
GradType,
HasEvaluateWithGradient,
true>
{
public:
// Reflect the existing Evaluate().
double Evaluate(const arma::mat& coordinates) const
typename MatType::elem_type Evaluate(const MatType& coordinates) const
{
return static_cast<const FunctionType*>(static_cast<const
Function<FunctionType>*>(this))->Evaluate(
coordinates);
return static_cast<const FunctionType*>(
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this))->Evaluate(coordinates);
}
};
@@ -109,8 +140,8 @@ Function<FunctionType>*>(this))->Evaluate(
* If we have EvaluateWithGradient() but no existing Evaluate(), add an
* Evaluate() without a using directive to make the base Evaluate() accessible.
*/
template<typename FunctionType>
class AddEvaluateConst<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddEvaluateConst<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -118,12 +149,14 @@ class AddEvaluateConst<FunctionType, true, false>
*
* @param coordinates Coordinates to evaluate the function at.
*/
double Evaluate(const arma::mat& coordinates) const
typename MatType::elem_type Evaluate(const MatType& coordinates) const
{
arma::mat gradient; // This will be ignored.
GradType gradient; // This will be ignored.
return static_cast<
const Function<FunctionType>*>(this)->EvaluateWithGradient(coordinates,
gradient);
const Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(coordinates,
gradient);
}
};
@@ -133,28 +166,43 @@ class AddEvaluateConst<FunctionType, true, false>
* otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::EvaluateWithGradientStaticForm>::value,
traits::TypedForms<MatType,
GradType>::template
EvaluateWithGradientStaticForm
>::value,
bool HasEvaluate =
traits::HasEvaluate<FunctionType,
traits::EvaluateStaticForm>::value>
traits::TypedForms<MatType, GradType>::template
EvaluateStaticForm
>::value>
class AddEvaluateStatic
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
static double Evaluate(traits::UnconstructableType&);
static typename MatType::elem_type Evaluate(traits::UnconstructableType&);
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType, bool HasEvaluateWithGradient>
class AddEvaluateStatic<FunctionType, HasEvaluateWithGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient>
class AddEvaluateStatic<FunctionType,
MatType,
GradType,
HasEvaluateWithGradient,
true>
{
public:
// Reflect the existing Evaluate().
static double Evaluate(const arma::mat& coordinates)
static typename MatType::elem_type Evaluate(
const MatType& coordinates)
{
return FunctionType::Evaluate(coordinates);
}
@@ -164,8 +212,8 @@ class AddEvaluateStatic<FunctionType, HasEvaluateWithGradient, true>
* If we have EvaluateWithGradient() but no existing Evaluate(), add an
* Evaluate() without a using directive to make the base Evaluate() accessible.
*/
template<typename FunctionType>
class AddEvaluateStatic<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddEvaluateStatic<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -173,9 +221,9 @@ class AddEvaluateStatic<FunctionType, true, false>
*
* @param coordinates Coordinates to evaluate the function at.
*/
static double Evaluate(const arma::mat& coordinates)
static typename MatType::elem_type Evaluate(const MatType& coordinates)
{
arma::mat gradient; // This will be ignored.
GradType gradient; // This will be ignored.
return FunctionType::EvaluateWithGradient(coordinates, gradient);
}
};
@@ -24,40 +24,55 @@ namespace ens {
* and Gradient(), or it will provide nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
// Check if there is at least one non-const Evaluate() or Gradient().
bool HasEvaluateGradient = traits::HasNonConstSignatures<
FunctionType,
traits::HasEvaluate,
traits::EvaluateForm,
traits::EvaluateConstForm,
traits::EvaluateStaticForm,
traits::TypedForms<MatType, GradType>::template EvaluateForm,
traits::TypedForms<MatType, GradType>::template EvaluateConstForm,
traits::TypedForms<MatType, GradType>::template EvaluateStaticForm,
traits::HasGradient,
traits::GradientForm,
traits::GradientConstForm,
traits::GradientStaticForm>::value,
traits::TypedForms<MatType, GradType>::template GradientForm,
traits::TypedForms<MatType, GradType>::template GradientConstForm,
traits::TypedForms<MatType, GradType>::template GradientStaticForm
>::value,
bool HasEvaluateWithGradient = traits::HasEvaluateWithGradient<
FunctionType,
traits::EvaluateWithGradientForm>::value>
traits::TypedForms<MatType, GradType>::template
EvaluateWithGradientForm>::value>
class AddEvaluateWithGradient
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
double EvaluateWithGradient(traits::UnconstructableType&);
typename MatType::elem_type EvaluateWithGradient(
traits::UnconstructableType&);
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType, bool HasEvaluateGradient>
class AddEvaluateWithGradient<FunctionType, HasEvaluateGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateGradient>
class AddEvaluateWithGradient<FunctionType,
MatType,
GradType,
HasEvaluateGradient,
true>
{
public:
// Reflect the existing EvaluateWithGradient().
double EvaluateWithGradient(const arma::mat& coordinates, arma::mat& gradient)
typename MatType::elem_type EvaluateWithGradient(
const MatType& coordinates, GradType& gradient)
{
return static_cast<FunctionType*>(
static_cast<Function<FunctionType>*>(this))->EvaluateWithGradient(
static_cast<Function<FunctionType,
MatType,
GradType>*>(this))->EvaluateWithGradient(
coordinates, gradient);
}
};
@@ -66,8 +81,8 @@ class AddEvaluateWithGradient<FunctionType, HasEvaluateGradient, true>
* If the FunctionType has Evaluate() and Gradient(), provide
* EvaluateWithGradient().
*/
template<typename FunctionType>
class AddEvaluateWithGradient<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddEvaluateWithGradient<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -77,12 +92,15 @@ class AddEvaluateWithGradient<FunctionType, true, false>
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
double EvaluateWithGradient(const arma::mat& coordinates,
arma::mat& gradient)
typename MatType::elem_type EvaluateWithGradient(const MatType& coordinates,
GradType& gradient)
{
const double objective =
static_cast<Function<FunctionType>*>(this)->Evaluate(coordinates);
static_cast<Function<FunctionType>*>(this)->Gradient(coordinates, gradient);
const typename MatType::elem_type objective =
static_cast<Function<FunctionType,
MatType, GradType>*>(this)->Evaluate(coordinates);
static_cast<Function<FunctionType,
MatType,
GradType>*>(this)->Gradient(coordinates, gradient);
return objective;
}
};
@@ -93,39 +111,54 @@ class AddEvaluateWithGradient<FunctionType, true, false>
* Evaluate() const and Gradient() const, or it will provide nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
// Check if there is at least one const Evaluate() or Gradient().
bool HasEvaluateGradient = traits::HasConstSignatures<
FunctionType,
traits::HasEvaluate,
traits::EvaluateConstForm,
traits::EvaluateStaticForm,
traits::TypedForms<MatType, GradType>::template EvaluateConstForm,
traits::TypedForms<MatType, GradType>::template EvaluateStaticForm,
traits::HasGradient,
traits::GradientConstForm,
traits::GradientStaticForm>::value,
traits::TypedForms<MatType, GradType>::template GradientConstForm,
traits::TypedForms<MatType, GradType>::template GradientStaticForm
>::value,
bool HasEvaluateWithGradient = traits::HasEvaluateWithGradient<
FunctionType,
traits::EvaluateWithGradientConstForm>::value>
traits::TypedForms<
MatType, GradType
>::template EvaluateWithGradientConstForm>::value>
class AddEvaluateWithGradientConst
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
double EvaluateWithGradient(traits::UnconstructableType&) const;
typename MatType::elem_type EvaluateWithGradient(
traits::UnconstructableType&) const;
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType, bool HasEvaluateGradient>
class AddEvaluateWithGradientConst<FunctionType, HasEvaluateGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateGradient>
class AddEvaluateWithGradientConst<FunctionType,
MatType,
GradType,
HasEvaluateGradient,
true>
{
public:
// Reflect the existing EvaluateWithGradient().
double EvaluateWithGradient(const arma::mat& coordinates, arma::mat& gradient)
const
typename MatType::elem_type EvaluateWithGradient(
const MatType& coordinates, GradType& gradient) const
{
return static_cast<const FunctionType*>(
static_cast<const Function<FunctionType>*>(this))->EvaluateWithGradient(
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this))->EvaluateWithGradient(
coordinates, gradient);
}
};
@@ -134,8 +167,8 @@ class AddEvaluateWithGradientConst<FunctionType, HasEvaluateGradient, true>
* If the FunctionType has Evaluate() const and Gradient() const, provide
* EvaluateWithGradient() const.
*/
template<typename FunctionType>
class AddEvaluateWithGradientConst<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddEvaluateWithGradientConst<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -145,13 +178,17 @@ class AddEvaluateWithGradientConst<FunctionType, true, false>
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
double EvaluateWithGradient(const arma::mat& coordinates,
arma::mat& gradient) const
typename MatType::elem_type EvaluateWithGradient(const MatType& coordinates,
GradType& gradient) const
{
const double objective =
static_cast<const Function<FunctionType>*>(this)->Evaluate(coordinates);
static_cast<const Function<FunctionType>*>(this)->Gradient(coordinates,
gradient);
const typename MatType::elem_type objective =
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this)->Evaluate(coordinates);
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this)->Gradient(coordinates,
gradient);
return objective;
}
};
@@ -163,32 +200,49 @@ class AddEvaluateWithGradientConst<FunctionType, true, false>
* otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateGradient =
traits::HasEvaluate<FunctionType,
traits::EvaluateStaticForm>::value &&
traits::TypedForms<MatType, GradType>::template
EvaluateStaticForm
>::value &&
traits::HasGradient<FunctionType,
traits::GradientStaticForm>::value,
traits::TypedForms<MatType, GradType>::template
GradientStaticForm
>::value,
bool HasEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::EvaluateWithGradientStaticForm>::value>
traits::TypedForms<MatType,
GradType>::template
EvaluateWithGradientStaticForm
>::value>
class AddEvaluateWithGradientStatic
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
static double EvaluateWithGradient(traits::UnconstructableType&);
static typename MatType::elem_type EvaluateWithGradient(
traits::UnconstructableType&);
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType, bool HasEvaluateGradient>
class AddEvaluateWithGradientStatic<FunctionType, HasEvaluateGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateGradient>
class AddEvaluateWithGradientStatic<FunctionType,
MatType,
GradType,
HasEvaluateGradient,
true>
{
public:
// Reflect the existing EvaluateWithGradient().
static double EvaluateWithGradient(const arma::mat& coordinates,
arma::mat& gradient)
static typename MatType::elem_type EvaluateWithGradient(
const MatType& coordinates, GradType& gradient)
{
return FunctionType::EvaluateWithGradient(coordinates, gradient);
}
@@ -198,8 +252,12 @@ class AddEvaluateWithGradientStatic<FunctionType, HasEvaluateGradient, true>
* If the FunctionType has static Evaluate() and static Gradient(), provide
* static EvaluateWithGradient().
*/
template<typename FunctionType>
class AddEvaluateWithGradientStatic<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddEvaluateWithGradientStatic<FunctionType,
MatType,
GradType,
true,
false>
{
public:
/**
@@ -209,10 +267,11 @@ class AddEvaluateWithGradientStatic<FunctionType, true, false>
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
static double EvaluateWithGradient(const arma::mat& coordinates,
arma::mat& gradient)
static typename MatType::elem_type EvaluateWithGradient(
const MatType& coordinates, GradType& gradient)
{
const double objective = FunctionType::Evaluate(coordinates);
const typename MatType::elem_type objective =
FunctionType::Evaluate(coordinates);
FunctionType::Gradient(coordinates, gradient);
return objective;
}
@@ -22,11 +22,16 @@ namespace ens {
* FunctionType has EvaluateWithGradient(), or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::EvaluateWithGradientForm>::value,
traits::TypedForms<MatType, GradType>::template
EvaluateWithGradientForm
>::value,
bool HasGradient = traits::HasGradient<FunctionType,
traits::GradientForm>::value>
traits::TypedForms<MatType, GradType>::template
GradientForm>::value>
class AddGradient
{
public:
@@ -37,15 +42,25 @@ class AddGradient
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType, bool HasEvaluateWithGradient>
class AddGradient<FunctionType, HasEvaluateWithGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient>
class AddGradient<FunctionType,
MatType,
GradType,
HasEvaluateWithGradient,
true>
{
public:
// Reflect the existing Gradient().
void Gradient(const arma::mat& coordinates, arma::mat& gradient)
void Gradient(const MatType& coordinates, GradType& gradient)
{
static_cast<FunctionType*>(static_cast<Function<FunctionType>*>(
this))->Gradient(coordinates, gradient);
static_cast<FunctionType*>(
static_cast<Function<FunctionType,
MatType,
GradType>*>(this))->Gradient(coordinates,
gradient);
}
};
@@ -53,8 +68,8 @@ class AddGradient<FunctionType, HasEvaluateWithGradient, true>
* If we have EvaluateWithGradient() but no existing Gradient(), add an
* Gradient() without a using directive to make the base Gradient() accessible.
*/
template<typename FunctionType>
class AddGradient<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddGradient<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -63,10 +78,12 @@ class AddGradient<FunctionType, true, false>
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
void Gradient(const arma::mat& coordinates, arma::mat& gradient)
void Gradient(const MatType& coordinates, GradType& gradient)
{
// The returned objective value will be ignored.
(void) static_cast<Function<FunctionType>*>(this)->EvaluateWithGradient(
(void) static_cast<Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(
coordinates, gradient);
}
};
@@ -76,11 +93,17 @@ class AddGradient<FunctionType, true, false>
* given FunctionType has EvaluateWithGradient() const, or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::EvaluateWithGradientConstForm>::value,
traits::TypedForms<MatType,
GradType>::template
EvaluateWithGradientConstForm
>::value,
bool HasGradient = traits::HasGradient<FunctionType,
traits::GradientConstForm>::value>
traits::TypedForms<MatType, GradType>::template GradientConstForm
>::value>
class AddGradientConst
{
public:
@@ -91,16 +114,25 @@ class AddGradientConst
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType, bool HasEvaluateWithGradient>
class AddGradientConst<FunctionType, HasEvaluateWithGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient>
class AddGradientConst<FunctionType,
MatType,
GradType,
HasEvaluateWithGradient,
true>
{
public:
// Reflect the existing Gradient().
void Gradient(const arma::mat& coordinates, arma::mat& gradient) const
void Gradient(const MatType& coordinates, GradType& gradient) const
{
static_cast<const FunctionType*>(static_cast<const
Function<FunctionType>*>(this))->Gradient(coordinates,
gradient);
static_cast<const FunctionType*>(
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this))->Gradient(coordinates,
gradient);
}
};
@@ -108,8 +140,8 @@ Function<FunctionType>*>(this))->Gradient(coordinates,
* If we have EvaluateWithGradient() but no existing Gradient(), add a
* Gradient() without a using directive to make the base Gradient() accessible.
*/
template<typename FunctionType>
class AddGradientConst<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddGradientConst<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -118,12 +150,14 @@ class AddGradientConst<FunctionType, true, false>
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
void Gradient(const arma::mat& coordinates, arma::mat& gradient) const
void Gradient(const MatType& coordinates, GradType& gradient) const
{
// The returned objective value will be ignored.
(void) static_cast<
const Function<FunctionType>*>(this)->EvaluateWithGradient(coordinates,
gradient);
const Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(coordinates,
gradient);
}
};
@@ -132,11 +166,17 @@ class AddGradientConst<FunctionType, true, false>
* given FunctionType has static EvaluateWithGradient(), or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::EvaluateWithGradientStaticForm>::value,
traits::TypedForms<MatType,
GradType>::template
EvaluateWithGradientStaticForm
>::value,
bool HasGradient = traits::HasGradient<FunctionType,
traits::GradientStaticForm>::value>
traits::TypedForms<MatType, GradType>::template GradientStaticForm
>::value>
class AddGradientStatic
{
public:
@@ -147,12 +187,19 @@ class AddGradientStatic
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType, bool HasEvaluateWithGradient>
class AddGradientStatic<FunctionType, HasEvaluateWithGradient, true>
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasEvaluateWithGradient>
class AddGradientStatic<FunctionType,
MatType,
GradType,
HasEvaluateWithGradient,
true>
{
public:
// Reflect the existing Gradient().
static void Gradient(const arma::mat& coordinates, arma::mat& gradient)
static void Gradient(const MatType& coordinates, GradType& gradient)
{
FunctionType::Gradient(coordinates, gradient);
}
@@ -162,8 +209,8 @@ class AddGradientStatic<FunctionType, HasEvaluateWithGradient, true>
* If we have EvaluateWithGradient() but no existing Gradient(), add a
* Gradient() without a using directive to make the base Gradient() accessible.
*/
template<typename FunctionType>
class AddGradientStatic<FunctionType, true, false>
template<typename FunctionType, typename MatType, typename GradType>
class AddGradientStatic<FunctionType, MatType, GradType, true, false>
{
public:
/**
@@ -172,7 +219,7 @@ class AddGradientStatic<FunctionType, true, false>
* @param coordinates Coordinates to evaluate the function at.
* @param gradient Matrix to store the gradient into.
*/
static void Gradient(const arma::mat& coordinates, arma::mat& gradient)
static void Gradient(const MatType& coordinates, GradType& gradient)
{
// The returned objective value will be ignored.
(void) FunctionType::EvaluateWithGradient(coordinates, gradient);
@@ -0,0 +1,251 @@
/**
* @file add_separable_evaluate.hpp
* @author Ryan Curtin
*
* Adds a separable Evaluate() function if a separable
* EvaluateWithGradient() function exists.
*
* 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_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_HPP
#define ENSMALLEN_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_HPP
#include "traits.hpp"
namespace ens {
/**
* The AddSeparableEvaluate mixin class will add a separable Evaluate()
* method if a separable EvaluateWithGradient() function exists, or nothing
* otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientForm
>::value,
bool HasSeparableEvaluate =
traits::HasEvaluate<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateForm>::value>
class AddSeparableEvaluate
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
typename MatType::elem_type Evaluate(traits::UnconstructableType&,
const size_t,
const size_t);
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient>
class AddSeparableEvaluate<FunctionType, MatType, GradType,
HasSeparableEvaluateWithGradient, true>
{
public:
// Reflect the existing Evaluate().
typename MatType::elem_type Evaluate(const MatType& coordinates,
const size_t begin,
const size_t batchSize)
{
return static_cast<FunctionType*>(
static_cast<Function<FunctionType,
MatType,
GradType>*>(this))->Evaluate(coordinates,
begin,
batchSize);
}
};
/**
* If we have a separable EvaluateWithGradient() but not a separable
* Evaluate(), add a separable Evaluate() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableEvaluate<FunctionType, MatType, GradType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given separable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
double Evaluate(const MatType& coordinates,
const size_t begin,
const size_t batchSize)
{
GradType gradient; // This will be ignored.
return static_cast<Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddSeparableEvaluateConst mixin class will add a separable const
* Evaluate() method if a separable const EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientConstForm>::value,
bool HasSeparableEvaluate =
traits::HasEvaluate<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateConstForm>::value>
class AddSeparableEvaluateConst
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
typename MatType::elem_type Evaluate(traits::UnconstructableType&,
const size_t,
const size_t) const;
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient>
class AddSeparableEvaluateConst<FunctionType, MatType, GradType,
HasSeparableEvaluateWithGradient, true>
{
public:
// Reflect the existing Evaluate().
typename MatType::elem_type Evaluate(const MatType& coordinates,
const size_t begin,
const size_t batchSize) const
{
return static_cast<const FunctionType*>(
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this))->Evaluate(coordinates,
begin,
batchSize);
}
};
/**
* If we have a separable const EvaluateWithGradient() but not a separable
* const Evaluate(), add a separable const Evaluate() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableEvaluateConst<FunctionType, MatType, GradType, true, false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given separable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
typename MatType::elem_type Evaluate(const MatType& coordinates,
const size_t begin,
const size_t batchSize) const
{
GradType gradient; // This will be ignored.
return static_cast<const Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddSeparableEvaluateStatic mixin class will add a separable static
* Evaluate() method if a separable static EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientStaticForm>::value,
bool HasSeparableEvaluate =
traits::HasEvaluate<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateStaticForm>::value>
class AddSeparableEvaluateStatic
{
public:
// Provide a dummy overload so the name 'Evaluate' exists for this object.
static typename MatType::elem_type Evaluate(traits::UnconstructableType&,
const size_t,
const size_t);
};
/**
* Reflect the existing Evaluate().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient>
class AddSeparableEvaluateStatic<FunctionType, MatType, GradType,
HasSeparableEvaluateWithGradient, true>
{
public:
// Reflect the existing Evaluate().
static typename MatType::elem_type Evaluate(const MatType& coordinates,
const size_t begin,
const size_t batchSize)
{
return FunctionType::Evaluate(coordinates, begin, batchSize);
}
};
/**
* If we have a separable EvaluateWithGradient() but not a separable
* Evaluate(), add a separable Evaluate() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableEvaluateStatic<FunctionType, MatType, GradType, true,
false>
{
public:
/**
* Return the objective function for the given coordinates, starting at the
* given separable function using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of first function to evaluate.
* @param batchSize Number of functions to evaluate.
*/
static typename MatType::elem_type Evaluate(const MatType& coordinates,
const size_t begin,
const size_t batchSize)
{
GradType gradient; // This will be ignored.
return FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
} // namespace ens
#endif
@@ -0,0 +1,308 @@
/**
* @file add_separable_evaluate_with_gradient.hpp
* @author Ryan Curtin
*
* Adds a separable EvaluateWithGradient() function if both a separable
* Evaluate() and a separable Gradient() function exist.
*
* 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_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_W_GRADIENT_HPP
#define ENSMALLEN_FUNCTION_ADD_DECOMPOSABLE_EVALUATE_W_GRADIENT_HPP
#include "traits.hpp"
namespace ens {
/**
* The AddSeparableEvaluateWithGradient mixin class will add a separable
* EvaluateWithGradient() method if a separable Evaluate() method and a
* separable Gradient() method exists, or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
// Check if there is at least one non-const Evaluate() or Gradient().
bool HasSeparableEvaluateGradient = traits::HasNonConstSignatures<
FunctionType,
traits::HasEvaluate,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateForm,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateConstForm,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateStaticForm,
traits::HasGradient,
traits::TypedForms<MatType, GradType>::template
SeparableGradientForm,
traits::TypedForms<MatType, GradType>::template
SeparableGradientConstForm,
traits::TypedForms<MatType, GradType>::template
SeparableGradientStaticForm>::value,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientForm>::value>
class AddSeparableEvaluateWithGradient
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
typename MatType::elem_type EvaluateWithGradient(
traits::UnconstructableType&,
const size_t,
const size_t);
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateGradient>
class AddSeparableEvaluateWithGradient<FunctionType, MatType, GradType,
HasSeparableEvaluateGradient, true>
{
public:
// Reflect the existing EvaluateWithGradient().
typename MatType::elem_type EvaluateWithGradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize)
{
return static_cast<FunctionType*>(
static_cast<Function<FunctionType,
MatType,
GradType>*>(this))->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* If we have a both separable Evaluate() and a separable Gradient() but
* not a separable EvaluateWithGradient(), add a separable
* EvaluateWithGradient() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableEvaluateWithGradient<FunctionType, MatType, GradType, true,
false>
{
public:
/**
* Return both the evaluated objective function and its gradient, storing the
* gradient in the given matrix, starting at the given separable function
* and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of separable function to begin with.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of separable functions to evaluate.
*/
typename MatType::elem_type EvaluateWithGradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize)
{
const typename MatType::elem_type objective =
static_cast<Function<FunctionType, MatType, GradType>*>(this)->Evaluate(
coordinates, begin, batchSize);
static_cast<Function<FunctionType, MatType, GradType>*>(this)->Gradient(
coordinates, begin, gradient, batchSize);
return objective;
}
};
/**
* The AddSeparableEvaluateWithGradientConst mixin class will add a
* separable const EvaluateWithGradient() method if both a separable const
* Evaluate() and a separable const Gradient() function exist, or nothing
* otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
// Check if there is at least one const Evaluate() or Gradient().
bool HasSeparableEvaluateGradient = traits::HasConstSignatures<
FunctionType,
traits::HasEvaluate,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateConstForm,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateStaticForm,
traits::HasGradient,
traits::TypedForms<MatType, GradType>::template
SeparableGradientConstForm,
traits::TypedForms<MatType, GradType>::template
SeparableGradientStaticForm>::value,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientConstForm>::value>
class AddSeparableEvaluateWithGradientConst
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
typename MatType::elem_type EvaluateWithGradient(
traits::UnconstructableType&,
const size_t,
const size_t) const;
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateGradient>
class AddSeparableEvaluateWithGradientConst<FunctionType, MatType, GradType,
HasSeparableEvaluateGradient, true>
{
public:
// Reflect the existing Evaluate().
typename MatType::elem_type EvaluateWithGradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize) const
{
return static_cast<const FunctionType*>(
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this))->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* If we have both a separable const Evaluate() and a separable const
* Gradient() but not a separable const EvaluateWithGradient(), add a
* separable const EvaluateWithGradient() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableEvaluateWithGradientConst<FunctionType, MatType, GradType,
true, false>
{
public:
/**
* Return both the evaluated objective function and its gradient, storing the
* gradient in the given matrix, starting at the given separable function
* and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of separable function to begin with.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of separable functions to evaluate.
*/
typename MatType::elem_type EvaluateWithGradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize) const
{
const typename MatType::elem_type objective =
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this)->Evaluate(coordinates,
begin, batchSize);
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this)->Gradient(coordinates,
begin, gradient, batchSize);
return objective;
}
};
/**
* The AddSeparableEvaluateWithGradientStatic mixin class will add a
* separable static EvaluateWithGradient() method if both a separable
* static Evaluate() and a separable static gradient() function exist, or
* nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateGradient =
traits::HasEvaluate<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateStaticForm>::value &&
traits::HasGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableGradientStaticForm>::value,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientStaticForm>::value>
class AddSeparableEvaluateWithGradientStatic
{
public:
// Provide a dummy overload so the name 'EvaluateWithGradient' exists for this
// object.
static typename MatType::elem_type EvaluateWithGradient(
traits::UnconstructableType&,
const size_t,
const size_t);
};
/**
* Reflect the existing EvaluateWithGradient().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateGradient>
class AddSeparableEvaluateWithGradientStatic<FunctionType, MatType, GradType,
HasSeparableEvaluateGradient, true>
{
public:
// Reflect the existing Evaluate().
static typename MatType::elem_type EvaluateWithGradient(
const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize)
{
return FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
/**
* If we have a separable static Evaluate() and a separable static
* Gradient() but not a separable static EvaluateWithGradient(), add a
* separable static Gradient() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableEvaluateWithGradientStatic<FunctionType, MatType, GradType,
true, false>
{
public:
/**
* Return both the evaluated objective function and its gradient, storing the
* gradient in the given matrix, starting at the given separable function
* and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of separable function to begin with.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of separable functions to evaluate.
*/
typename MatType::elem_type EvaluateWithGradient(
const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize) const
{
const typename MatType::elem_type objective = FunctionType::Evaluate(
coordinates, begin, batchSize);
FunctionType::Gradient(coordinates, begin, gradient, batchSize);
return objective;
}
};
} // namespace ens
#endif
@@ -0,0 +1,254 @@
/**
* @file add_separable_gradient.hpp
* @author Ryan Curtin
*
* Adds a separable Gradient() function if a separable
* EvaluateWithGradient() function exists.
*
* 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_FUNCTION_ADD_DECOMPOSABLE_GRADIENT_HPP
#define ENSMALLEN_FUNCTION_ADD_DECOMPOSABLE_GRADIENT_HPP
#include "traits.hpp"
namespace ens {
/**
* The AddSeparableGradient mixin class will add a separable Gradient()
* method if a separable EvaluateWithGradient() function exists, or nothing
* otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientForm>::value,
bool HasSeparableGradient =
traits::HasGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableGradientForm>::value>
class AddSeparableGradient
{
public:
// Provide a dummy overload so the name 'Gradient' exists for this object.
void Gradient(traits::UnconstructableType&, const size_t, const size_t);
};
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient>
class AddSeparableGradient<FunctionType, MatType, GradType,
HasSeparableEvaluateWithGradient, true>
{
public:
// Reflect the existing Gradient().
void Gradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize)
{
static_cast<FunctionType*>(
static_cast<Function<FunctionType,
MatType,
GradType>*>(this))->Gradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* If we have a separable EvaluateWithGradient() but not a separable
* Gradient(), add a separable Evaluate() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableGradient<FunctionType, MatType, GradType, true, false>
{
public:
/**
* Calculate the gradient and store it in the given matrix, starting at the
* given separable function index and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of separable function to start at.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of separable functions to calculate for.
*/
void Gradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize)
{
// The returned objective value will be ignored.
(void) static_cast<Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddSeparableGradientConst mixin class will add a separable const
* Gradient() method if a separable const EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientConstForm>::value,
bool HasSeparableGradient =
traits::HasGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableGradientConstForm>::value>
class AddSeparableGradientConst
{
public:
// Provide a dummy overload so the name 'Gradient' exists for this object.
void Gradient(traits::UnconstructableType&, const size_t, const size_t) const;
};
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient>
class AddSeparableGradientConst<FunctionType, MatType, GradType,
HasSeparableEvaluateWithGradient, true>
{
public:
// Reflect the existing Gradient().
void Gradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize) const
{
static_cast<const FunctionType*>(
static_cast<const Function<FunctionType,
MatType,
GradType>*>(this))->Gradient(coordinates,
begin, gradient, batchSize);
}
};
/**
* If we have a separable const EvaluateWithGradient() but not a separable
* const Gradient(), add a separable const Gradient() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableGradientConst<FunctionType, MatType, GradType, true, false>
{
public:
/**
* Calculate the gradient and store it in the given matrix, starting at the
* given separable function index and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of separable function to start at.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of separable functions to calculate for.
*/
void Gradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize) const
{
// The returned objective value will be ignored.
(void) static_cast<
const Function<FunctionType,
MatType,
GradType>*>(this)->EvaluateWithGradient(
coordinates, begin, gradient, batchSize);
}
};
/**
* The AddSeparableEvaluateStatic mixin class will add a separable static
* Gradient() method if a separable static EvaluateWithGradient() function
* exists, or nothing otherwise.
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient =
traits::HasEvaluateWithGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientStaticForm>::value,
bool HasSeparableGradient =
traits::HasGradient<FunctionType,
traits::TypedForms<MatType, GradType>::template
SeparableGradientStaticForm>::value>
class AddSeparableGradientStatic
{
public:
// Provide a dummy overload so the name 'Gradient' exists for this object.
static void Gradient(traits::UnconstructableType&,
const size_t,
const size_t);
};
/**
* Reflect the existing Gradient().
*/
template<typename FunctionType,
typename MatType,
typename GradType,
bool HasSeparableEvaluateWithGradient>
class AddSeparableGradientStatic<FunctionType, MatType, GradType,
HasSeparableEvaluateWithGradient, true>
{
public:
// Reflect the existing Gradient().
static void Gradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize)
{
FunctionType::Gradient(coordinates, begin, gradient, batchSize);
}
};
/**
* If we have a separable EvaluateWithGradient() but not a separable
* Gradient(), add a separable Gradient() method.
*/
template<typename FunctionType, typename MatType, typename GradType>
class AddSeparableGradientStatic<FunctionType, MatType, GradType, true,
false>
{
public:
/**
* Calculate the gradient and store it in the given matrix, starting at the
* given separable function index and using the given batch size.
*
* @param coordinates Coordinates to evaluate the function at.
* @param begin Index of separable function to start at.
* @param gradient Matrix to store the gradient into.
* @param batchSize Number of separable functions to calculate for.
*/
static void Gradient(const MatType& coordinates,
const size_t begin,
GradType& gradient,
const size_t batchSize)
{
// The returned objective value will be ignored.
(void) FunctionType::EvaluateWithGradient(coordinates, begin, gradient,
batchSize);
}
};
} // namespace ens
#endif
@@ -0,0 +1,176 @@
/**
* @file arma_traits.hpp
* @author Ryan Curtin
*
* Given an Armadillo type, determine its "true" base type.
*/
#ifndef ENSMALLEN_FUNCTION_ARMA_TRAITS_HPP
#define ENSMALLEN_FUNCTION_ARMA_TRAITS_HPP
namespace ens {
/**
* Extract the base type of a matrix (i.e. if it is a column, return the matrix
* type). If the type is unknown (or not a derived type) we just return the
* type itself as the typedef BaseMatType.
*/
template<typename MatType>
struct MatTypeTraits
{
typedef MatType BaseMatType;
};
template<typename eT>
struct MatTypeTraits<arma::Col<eT>>
{
typedef arma::Mat<eT> BaseMatType;
};
template<typename eT>
struct MatTypeTraits<arma::Row<eT>>
{
typedef arma::Mat<eT> BaseMatType;
};
template<typename eT>
struct MatTypeTraits<arma::SpCol<eT>>
{
typedef arma::SpMat<eT> BaseMatType;
};
template<typename eT>
struct MatTypeTraits<arma::SpRow<eT>>
{
typedef arma::SpMat<eT> BaseMatType;
};
/**
* Disable usage of arma::subviews and related types for optimizers. It might
* be nice to also explicitly disable Armadillo expressions, but we'll hope for
* now nobody even tries that, since those aren't even lvalues and thus can't
* really work.
*/
template<typename eT>
struct MatTypeTraits<arma::subview<eT>>
{
static_assert(sizeof(arma::subview<eT>) == 0,
"Armadillo subviews cannot be passed to Optimize()! Create a matrix "
"or a matrix alias instead!");
};
template<typename eT>
struct MatTypeTraits<arma::subview_col<eT>>
{
static_assert(sizeof(arma::subview_col<eT>) == 0,
"Armadillo subviews cannot be passed to Optimize()! Create a matrix "
"or a matrix alias instead!");
};
template<typename eT>
struct MatTypeTraits<arma::SpSubview<eT>>
{
static_assert(sizeof(arma::SpSubview<eT>) == 0,
"Armadillo subviews cannot be passed to Optimize()! Create a matrix "
"or a matrix alias instead!");
};
#if ((ARMA_VERSION_MAJOR >= 10) || \
((ARMA_VERSION_MAJOR == 9) && (ARMA_VERSION_MINOR >= 869)))
// Armadillo 9.869+ has SpSubview_col and SpSubview_row
template<typename eT>
struct MatTypeTraits<arma::SpSubview_col<eT>>
{
static_assert(sizeof(arma::SpSubview_col<eT>) == 0,
"Armadillo subviews cannot be passed to Optimize()! Create a matrix "
"or a matrix alias instead!");
};
template<typename eT>
struct MatTypeTraits<arma::SpSubview_row<eT>>
{
static_assert(sizeof(arma::SpSubview_row<eT>) == 0,
"Armadillo subviews cannot be passed to Optimize()! Create a matrix "
"or a matrix alias instead!");
};
#endif
template<typename eT>
struct MatTypeTraits<arma::Cube<eT>>
{
static_assert(sizeof(arma::Cube<eT>) == 0,
"Armadillo cubes cannot be passed to Optimize()! Create a matrix "
"or a matrix alias instead!");
};
/**
* Issue a fatal error if the type is not an Armadillo double or floating point
* sparse or dense matrix.
*/
template<typename MatType>
void RequireDenseFloatingPointType()
{
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(sizeof(MatType) == 0,
"The given MatType must be arma::mat or arma::fmat or it is not known "
"to work! If you would like to try anyway, set the preprocessor macro "
"ENS_DISABLE_TYPE_CHECKS before including ensmallen.hpp. However, you "
"get to pick up all the pieces if there is a failure!");
#endif
}
template<>
inline void RequireDenseFloatingPointType<arma::mat>() { }
template<>
inline void RequireDenseFloatingPointType<arma::fmat>() { }
template<typename MatType>
void RequireFloatingPointType()
{
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(sizeof(MatType) == 0,
"The given MatType must be arma::mat, arma::fmat, arma::sp_mat, or "
"arma::sp_fmat, or it is not known to work! If you would like to try "
"anyway, set the preprocessor macro ENS_DISABLE_TYPE_CHECKS before "
"including ensmallen.hpp. However, you get to pick up all the pieces if "
"there is a failure!");
#endif
}
template<>
inline void RequireFloatingPointType<arma::mat>() { }
template<>
inline void RequireFloatingPointType<arma::fmat>() { }
template<>
inline void RequireFloatingPointType<arma::sp_mat>() { }
template<>
inline void RequireFloatingPointType<arma::sp_fmat>() { }
/**
* Require that the internal element type of the matrix type and gradient type
* are the same. A static_assert() will fail if not.
*/
template<typename MatType, typename GradType>
void RequireSameInternalTypes()
{
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(std::is_same<typename MatType::elem_type,
typename GradType::elem_type>::value,
"The internal element types of the given MatType and GradType must be "
"identical, or it is not known to work! If you would like to try "
"anyway, set the preprocessor macro ENS_DISABLE_TYPE_CHECKS before "
"including ensmallen.hpp. However, you get to pick up all the pieces if "
"there is a failure!");
#endif
}
} // namespace ens
#endif
@@ -105,6 +105,27 @@ struct MethodFormDetector<Class, MethodForm, 7>
//! Utility struct for checking signatures.
template<typename U, U> struct SigCheck : std::true_type {};
template<typename... Args>
struct pack {};
template<typename Func>
struct FunctionTypes {};
template<typename R, typename... A>
struct FunctionTypes<R(A...)>
{
typedef R Ret;
using Args = pack<A...>;
};
template<typename R, typename C, typename... A>
struct FunctionTypes<R(C::*)(A...)>
{
typedef R Ret;
typedef C Class;
using Args = pack<A...>;
};
} // namespace sfinae
} // namespace ens
@@ -133,7 +154,9 @@ struct NAME \
< \
T, \
sig, \
std::integral_constant<bool, SigCheck<sig, &T::FUNC>::value> \
std::is_same<decltype(std::declval<T>().FUNC(std::declval< \
ens::sfinae::FunctionTypes<sig>::A...>()...)), \
ens::sfinae::FunctionTypes<sig>::Ret>::type> \
> : std::true_type {};
/**
@@ -257,9 +280,9 @@ struct NAME \
ENS_HAS_METHOD_FORM_BASE(ENS_SINGLE_ARG(METHOD), ENS_SINGLE_ARG(NAME), 7)
/**
* ENS_HAS_EXACT_METHOD_FORM generates a template that allows to check at compile
* time whether a given class has a method of the requested form. For example,
* for the following class
* ENS_HAS_EXACT_METHOD_FORM generates a template that allows to check at
* compile time whether a given class has a method of the requested form. For
* example, for the following class
*
* class A
* {
@@ -291,37 +314,4 @@ struct NAME \
#define ENS_HAS_EXACT_METHOD_FORM(METHOD, NAME) \
ENS_HAS_METHOD_FORM_BASE(ENS_SINGLE_ARG(METHOD), ENS_SINGLE_ARG(NAME), 0)
/**
* A version of ENS_HAS_METHOD_FORM() where the maximum number of extra arguments is
* set to the default of 7.
*
* ENS_HAS_METHOD_FORM generates a template that allows to check at compile time
* whether a given class has a method of the requested form. For example, for
* the following class
*
* class A
* {
* public:
* ...
* Train(const arma::mat&, const arma::Row<size_t>&, double);
* ...
* };
*
* and the following form of Train methods
*
* template<typename Class, typename...Ts>
* using TrainForm =
* void(Class::*)(const arma::mat&, const arma::Row<size_t>&, Ts...);
*
* we can check whether the class A has a Train method of the specified form:
*
* ENS_HAS_METHOD_FORM(Train, HasTrain);
* static_assert(HasTrain<A, TrainFrom>::value, "value should be true");
*
* The implementation is analogous to implementation of the macro ENS_HAS_MEM_FUNC.
*
* @param METHOD The name of the method to check for.
* @param NAME The name of the struct to construct.
*/
#endif
+184 -110
View File
@@ -25,13 +25,16 @@ namespace traits {
*
* This is required by the FunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckEvaluate
{
const static bool value =
HasEvaluate<FunctionType, EvaluateForm>::value ||
HasEvaluate<FunctionType, EvaluateConstForm>::value ||
HasEvaluate<FunctionType, EvaluateStaticForm>::value;
HasEvaluate<FunctionType,
TypedForms<MatType, GradType>::template EvaluateForm>::value ||
HasEvaluate<FunctionType,
TypedForms<MatType, GradType>::template EvaluateConstForm>::value ||
HasEvaluate<FunctionType,
TypedForms<MatType, GradType>::template EvaluateStaticForm>::value;
};
/**
@@ -39,69 +42,84 @@ struct CheckEvaluate
*
* This is required by the FunctionType API.
*/
template <typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckGradient
{
const static bool value =
HasGradient<FunctionType, GradientForm>::value ||
HasGradient<FunctionType, GradientConstForm>::value ||
HasGradient<FunctionType, GradientStaticForm>::value;
HasGradient<FunctionType,
TypedForms<MatType, GradType>::template GradientForm>::value ||
HasGradient<FunctionType,
TypedForms<MatType, GradType>::template GradientConstForm>::value ||
HasGradient<FunctionType,
TypedForms<MatType, GradType>::template GradientStaticForm>::value;
};
/**
* Check if a suitable overload of NumFunctions() is available.
*
* This is required by the DecomposableFunctionType API.
* This is required by the SeparableFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckNumFunctions
{
const static bool value =
HasNumFunctions<FunctionType, NumFunctionsForm>::value ||
HasNumFunctions<FunctionType, NumFunctionsConstForm>::value ||
HasNumFunctions<FunctionType, NumFunctionsStaticForm>::value;
HasNumFunctions<FunctionType, TypedForms<MatType, GradType>::template
NumFunctionsForm>::value ||
HasNumFunctions<FunctionType, TypedForms<MatType, GradType>::template
NumFunctionsConstForm>::value ||
HasNumFunctions<FunctionType, TypedForms<MatType, GradType>::template
NumFunctionsStaticForm>::value;
};
/**
* Check if a suitable overload of Shuffle() is available.
*
* This is required by the DecomposableFunctionType API.
* This is required by the SeparableFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckShuffle
{
const static bool value =
HasShuffle<FunctionType, ShuffleForm>::value ||
HasShuffle<FunctionType, ShuffleConstForm>::value ||
HasShuffle<FunctionType, ShuffleStaticForm>::value;
HasShuffle<FunctionType, TypedForms<MatType, GradType>::template
ShuffleForm>::value ||
HasShuffle<FunctionType, TypedForms<MatType, GradType>::template
ShuffleConstForm>::value ||
HasShuffle<FunctionType, TypedForms<MatType, GradType>::template
ShuffleStaticForm>::value;
};
/**
* Check if a suitable decomposable overload of Evaluate() is available.
* Check if a suitable separable overload of Evaluate() is available.
*
* This is required by the DecomposableFunctionType API.
* This is required by the SeparableFunctionType API.
*/
template<typename FunctionType>
struct CheckDecomposableEvaluate
template<typename FunctionType, typename MatType, typename GradType>
struct CheckSeparableEvaluate
{
const static bool value =
HasEvaluate<FunctionType, DecomposableEvaluateForm>::value ||
HasEvaluate<FunctionType, DecomposableEvaluateConstForm>::value ||
HasEvaluate<FunctionType, DecomposableEvaluateStaticForm>::value;
HasEvaluate<FunctionType, TypedForms<MatType, GradType>::template
SeparableEvaluateForm>::value ||
HasEvaluate<FunctionType, TypedForms<MatType, GradType>::template
SeparableEvaluateConstForm>::value ||
HasEvaluate<FunctionType, TypedForms<MatType, GradType>::template
SeparableEvaluateStaticForm>::value;
};
/**
* Check if a suitable decomposable overload of Gradient() is available.
* Check if a suitable separable overload of Gradient() is available.
*
* This is required by the DecomposableFunctionType API.
* This is required by the SeparableFunctionType API.
*/
template <typename FunctionType>
struct CheckDecomposableGradient
template<typename FunctionType, typename MatType, typename GradType>
struct CheckSeparableGradient
{
const static bool value =
HasGradient<FunctionType, DecomposableGradientForm>::value ||
HasGradient<FunctionType, DecomposableGradientConstForm>::value ||
HasGradient<FunctionType, DecomposableGradientStaticForm>::value;
HasGradient<FunctionType, TypedForms<MatType, GradType>::template
SeparableGradientForm>::value ||
HasGradient<FunctionType, TypedForms<MatType, GradType>::template
SeparableGradientConstForm>::value ||
HasGradient<FunctionType, TypedForms<MatType, GradType>::template
SeparableGradientStaticForm>::value;
};
/**
@@ -109,13 +127,16 @@ struct CheckDecomposableGradient
*
* This is required by the ConstrainedFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckNumConstraints
{
const static bool value =
HasNumConstraints<FunctionType, NumConstraintsForm>::value ||
HasNumConstraints<FunctionType, NumConstraintsConstForm>::value ||
HasNumConstraints<FunctionType, NumConstraintsStaticForm>::value;
HasNumConstraints<FunctionType, TypedForms<MatType, GradType>::template
NumConstraintsForm>::value ||
HasNumConstraints<FunctionType, TypedForms<MatType, GradType>::template
NumConstraintsConstForm>::value ||
HasNumConstraints<FunctionType, TypedForms<MatType, GradType>::template
NumConstraintsStaticForm>::value;
};
/**
@@ -123,13 +144,19 @@ struct CheckNumConstraints
*
* This is required by the ConstrainedFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckEvaluateConstraint
{
const static bool value =
HasEvaluateConstraint<FunctionType, EvaluateConstraintForm>::value ||
HasEvaluateConstraint<FunctionType, EvaluateConstraintConstForm>::value ||
HasEvaluateConstraint<FunctionType, EvaluateConstraintStaticForm>::value;
HasEvaluateConstraint<FunctionType,
TypedForms<MatType, GradType>::template
EvaluateConstraintForm>::value ||
HasEvaluateConstraint<FunctionType,
TypedForms<MatType, GradType>::template
EvaluateConstraintConstForm>::value ||
HasEvaluateConstraint<FunctionType,
TypedForms<MatType, GradType>::template
EvaluateConstraintStaticForm>::value;
};
/**
@@ -137,13 +164,19 @@ struct CheckEvaluateConstraint
*
* This is required by the ConstrainedFunctionType API.
*/
template <typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckGradientConstraint
{
const static bool value =
HasGradientConstraint<FunctionType, GradientConstraintForm>::value ||
HasGradientConstraint<FunctionType, GradientConstraintConstForm>::value ||
HasGradientConstraint<FunctionType, GradientConstraintStaticForm>::value;
HasGradientConstraint<FunctionType,
TypedForms<MatType, GradType>::template
GradientConstraintForm>::value ||
HasGradientConstraint<FunctionType,
TypedForms<MatType, GradType>::template
GradientConstraintConstForm>::value ||
HasGradientConstraint<FunctionType,
TypedForms<MatType, GradType>::template
GradientConstraintStaticForm>::value;
};
/**
@@ -152,13 +185,16 @@ struct CheckGradientConstraint
*
* This is required by the SparseFunctionType API.
*/
template <typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckSparseGradient
{
const static bool value =
HasGradient<FunctionType, SparseGradientForm>::value ||
HasGradient<FunctionType, SparseGradientConstForm>::value ||
HasGradient<FunctionType, SparseGradientStaticForm>::value;
HasGradient<FunctionType, TypedForms<MatType, GradType>::template
SparseGradientForm>::value ||
HasGradient<FunctionType, TypedForms<MatType, GradType>::template
SparseGradientConstForm>::value ||
HasGradient<FunctionType, TypedForms<MatType, GradType>::template
SparseGradientStaticForm>::value;
};
/**
@@ -166,13 +202,16 @@ struct CheckSparseGradient
*
* This is required by the ResolvableFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckNumFeatures
{
const static bool value =
HasNumFeatures<FunctionType, NumFeaturesForm>::value ||
HasNumFeatures<FunctionType, NumFeaturesConstForm>::value ||
HasNumFeatures<FunctionType, NumFeaturesStaticForm>::value;
HasNumFeatures<FunctionType, TypedForms<MatType, GradType>::template
NumFeaturesForm>::value ||
HasNumFeatures<FunctionType, TypedForms<MatType, GradType>::template
NumFeaturesConstForm>::value ||
HasNumFeatures<FunctionType, TypedForms<MatType, GradType>::template
NumFeaturesStaticForm>::value;
};
/**
@@ -180,13 +219,16 @@ struct CheckNumFeatures
*
* This is required by the ResolvableFunctionType API.
*/
template <typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckPartialGradient
{
const static bool value =
HasPartialGradient<FunctionType, PartialGradientForm>::value ||
HasPartialGradient<FunctionType, PartialGradientConstForm>::value ||
HasPartialGradient<FunctionType, PartialGradientStaticForm>::value;
HasPartialGradient<FunctionType, TypedForms<MatType, GradType>::template
PartialGradientForm>::value ||
HasPartialGradient<FunctionType, TypedForms<MatType, GradType>::template
PartialGradientConstForm>::value ||
HasPartialGradient<FunctionType, TypedForms<MatType, GradType>::template
PartialGradientStaticForm>::value;
};
/**
@@ -194,207 +236,239 @@ struct CheckPartialGradient
*
* This is required by the FunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
struct CheckEvaluateWithGradient
{
const static bool value =
HasEvaluateWithGradient<FunctionType, EvaluateWithGradientForm>::value ||
HasEvaluateWithGradient<FunctionType,
EvaluateWithGradientConstForm>::value ||
TypedForms<MatType, GradType>::template
EvaluateWithGradientForm>::value ||
HasEvaluateWithGradient<FunctionType,
EvaluateWithGradientStaticForm>::value;
TypedForms<MatType, GradType>::template
EvaluateWithGradientConstForm>::value ||
HasEvaluateWithGradient<FunctionType,
TypedForms<MatType, GradType>::template
EvaluateWithGradientStaticForm>::value;
};
/**
* Check if a suitable decomposable overload of EvaluateWithGradient() is
* Check if a suitable separable overload of EvaluateWithGradient() is
* available.
*
* This is required by the FunctionType API.
*/
template<typename FunctionType>
struct CheckDecomposableEvaluateWithGradient
template<typename FunctionType, typename MatType, typename GradType>
struct CheckSeparableEvaluateWithGradient
{
const static bool value =
HasEvaluateWithGradient<FunctionType,
DecomposableEvaluateWithGradientForm>::value ||
TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientForm>::value ||
HasEvaluateWithGradient<FunctionType,
DecomposableEvaluateWithGradientConstForm>::value ||
TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientConstForm>::value ||
HasEvaluateWithGradient<FunctionType,
DecomposableEvaluateWithGradientStaticForm>::value;
TypedForms<MatType, GradType>::template
SeparableEvaluateWithGradientStaticForm>::value;
};
/**
* Perform checks for the regular FunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
inline void CheckFunctionTypeAPI()
{
static_assert(CheckEvaluate<FunctionType>::value,
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(CheckEvaluate<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of Evaluate(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the FunctionType API; see the optimizer tutorial for details.");
static_assert(CheckGradient<FunctionType>::value,
static_assert(CheckGradient<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of Gradient(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the FunctionType API; see the optimizer tutorial for details.");
static_assert(CheckEvaluateWithGradient<FunctionType>::value,
static_assert(
CheckEvaluateWithGradient<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of "
"EvaluateWithGradient(). Please check that the FunctionType fully "
"satisfies the requirements of the FunctionType API; see the optimizer "
"tutorial for more details.");
#endif
}
/**
* Perform checks for the DecomposableFunctionType API.
* Perform checks for the SeparableFunctionType API.
*/
template<typename FunctionType>
inline void CheckDecomposableFunctionTypeAPI()
template<typename FunctionType, typename MatType, typename GradType>
inline void CheckSeparableFunctionTypeAPI()
{
static_assert(CheckDecomposableEvaluate<FunctionType>::value,
"The FunctionType does not have a correct definition of a decomposable "
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(CheckSeparableEvaluate<FunctionType,
MatType,
GradType>::value,
"The FunctionType does not have a correct definition of a separable "
"Evaluate() method. Please check that the FunctionType fully satisfies"
" the requirements of the DecomposableFunctionType API; see the optimizer"
" the requirements of the SeparableFunctionType API; see the optimizer"
" tutorial for more details.");
static_assert(CheckDecomposableGradient<FunctionType>::value,
"The FunctionType does not have a correct definition of a decomposable "
static_assert(CheckSeparableGradient<FunctionType,
MatType,
GradType>::value,
"The FunctionType does not have a correct definition of a separable "
"Gradient() method. Please check that the FunctionType fully satisfies"
" the requirements of the DecomposableFunctionType API; see the optimizer"
" the requirements of the SeparableFunctionType API; see the optimizer"
" tutorial for more details.");
static_assert(CheckDecomposableEvaluateWithGradient<FunctionType>::value,
"The FunctionType does not have a correct definition of a decomposable "
static_assert(CheckSeparableEvaluateWithGradient<FunctionType,
MatType,
GradType>::value,
"The FunctionType does not have a correct definition of a separable "
"EvaluateWithGradient() method. Please check that the FunctionType "
"fully satisfies the requirements of the DecomposableFunctionType API; "
"fully satisfies the requirements of the SeparableFunctionType API; "
"see the optimizer tutorial for more details.");
static_assert(CheckNumFunctions<FunctionType>::value,
static_assert(CheckNumFunctions<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of NumFunctions(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the DecomposableFunctionType API; see the optimizer tutorial for more "
"the SeparableFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckShuffle<FunctionType>::value,
static_assert(CheckShuffle<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of Shuffle(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the DecomposableFunctionType API; see the optimizer tutorial for more "
"the SeparableFunctionType API; see the optimizer tutorial for more "
"details.");
#endif
}
/**
* Perform checks for the SparseFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
inline void CheckSparseFunctionTypeAPI()
{
static_assert(CheckNumFunctions<FunctionType>::value,
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(CheckNumFunctions<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of NumFunctions(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the SparseFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckDecomposableEvaluate<FunctionType>::value,
static_assert(CheckSeparableEvaluate<FunctionType,
MatType,
GradType>::value,
"The FunctionType does not have a correct definition of Evaluate(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the SparseFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckSparseGradient<FunctionType>::value,
static_assert(CheckSparseGradient<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of a sparse "
"Gradient() method. Please check that the FunctionType fully satisfies "
"the requirements of the SparseFunctionType API; see the optimizer "
"tutorial for more details.");
#endif
}
/**
* Perform checks for the NonDifferentiableFunctionType API.
* Perform checks for the ArbitraryFunctionType API.
*/
template<typename FunctionType>
inline void CheckNonDifferentiableFunctionTypeAPI()
template<typename FunctionType, typename MatType>
inline void CheckArbitraryFunctionTypeAPI()
{
static_assert(CheckEvaluate<FunctionType>::value,
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(CheckEvaluate<FunctionType, MatType, MatType>::value,
"The FunctionType does not have a correct definition of Evaluate(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the NonDifferentiableFunctionType API; see the optimizer tutorial for "
"the ArbitraryFunctionType API; see the optimizer tutorial for "
"more details.");
#endif
}
/**
* Perform checks for the ResolvableFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
inline void CheckResolvableFunctionTypeAPI()
{
static_assert(CheckNumFeatures<FunctionType>::value,
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(CheckNumFeatures<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of NumFeatures(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the ResolvableFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckEvaluate<FunctionType>::value,
static_assert(CheckEvaluate<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of Evaluate(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the ResolvableFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckPartialGradient<FunctionType>::value,
static_assert(CheckPartialGradient<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of a partial "
"Gradient() function. Please check that the FunctionType fully satisfies "
"the requirements of the ResolvableFunctionType API; see the optimizer "
"tutorial for more details.");
#endif
}
/**
* Perform checks for the ConstrainedFunctionType API.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
inline void CheckConstrainedFunctionTypeAPI()
{
static_assert(CheckEvaluate<FunctionType>::value,
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(CheckEvaluate<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of Evaluate(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the ConstrainedFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckGradient<FunctionType>::value,
static_assert(CheckGradient<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of Gradient(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the ConstrainedFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckNumConstraints<FunctionType>::value,
static_assert(CheckNumConstraints<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of NumConstraints()."
" Please check that the FunctionType fully satisfies the requirements of "
"the ConstrainedFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckEvaluateConstraint<FunctionType>::value,
static_assert(CheckEvaluateConstraint<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of "
"EvaluateConstraint(). Please check that the FunctionType fully satisfies"
" the ConstrainedFunctionType API; see the optimizer tutorial for more "
"details.");
static_assert(CheckGradientConstraint<FunctionType>::value,
static_assert(CheckGradientConstraint<FunctionType, MatType, GradType>::value,
"The FunctionType does not have a correct definition of "
"GradientConstraint(). Please check that the FunctionType fully satisfies"
" the ConstrainedFunctionType API; see the optimizer tutorial for more "
"details.");
#endif
}
/**
* Perform checks for the NonDifferentiableDecomposableFunctionType API. (I
* Perform checks for the ArbitrarySeparableFunctionType API. (I
* know, it is a long name...)
*/
template<typename FunctionType>
inline void CheckNonDifferentiableDecomposableFunctionTypeAPI()
template<typename FunctionType, typename MatType>
inline void CheckArbitrarySeparableFunctionTypeAPI()
{
static_assert(CheckDecomposableEvaluate<FunctionType>::value,
#ifndef ENS_DISABLE_TYPE_CHECKS
static_assert(CheckSeparableEvaluate<FunctionType,
MatType,
MatType>::value,
"The FunctionType does not have a correct definition of Evaluate(). "
"Please check that the FunctionType fully satisfies the requirements of "
"the NonDifferentiableDecomposableFunctionType API; see the optimizer "
"the ArbitrarySeparableFunctionType API; see the optimizer "
"tutorial for more details.");
#endif
}
} // namespace traits
+263 -166
View File
@@ -14,6 +14,7 @@
#define ENSMALLEN_FUNCTION_TRAITS_HPP
#include "sfinae_utility.hpp"
#include "arma_traits.hpp"
namespace ens {
namespace traits {
@@ -38,211 +39,253 @@ ENS_HAS_EXACT_METHOD_FORM(GradientConstraint, HasGradientConstraint)
ENS_HAS_EXACT_METHOD_FORM(NumFeatures, HasNumFeatures)
//! Detect a PartialGradient() method.
ENS_HAS_EXACT_METHOD_FORM(PartialGradient, HasPartialGradient)
//! Detect an MaxIterations() method.
ENS_HAS_EXACT_METHOD_FORM(MaxIterations, HasMaxIterations)
//! Detect an ResetPolicy() method.
ENS_HAS_EXACT_METHOD_FORM(ResetPolicy, HasResetPolicy)
//! Detect an BatchSize() method.
ENS_HAS_EXACT_METHOD_FORM(BatchSize, HasBatchSize)
//! This is the form of a non-const Evaluate() method.
template<typename FunctionType>
using EvaluateForm = double(FunctionType::*)(const arma::mat&);
template<typename MatType, typename GradType>
struct TypedForms
{
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
//! This is the form of a const Evaluate() method.
template<typename FunctionType>
using EvaluateConstForm =
double(FunctionType::*)(const arma::mat&) const;
//! This is the form of a non-const Evaluate() method.
template<typename FunctionType>
using EvaluateForm =
typename BaseMatType::elem_type(FunctionType::*)(const BaseMatType&);
//! This is the form of a static Evaluate() method.
template<typename FunctionType>
using EvaluateStaticForm = double(*)(const arma::mat&);
//! This is the form of a const Evaluate() method.
template<typename FunctionType>
using EvaluateConstForm = typename BaseMatType::elem_type(FunctionType::*)(
const BaseMatType&) const;
//! This is the form of a non-const Gradient() method.
template<typename FunctionType>
using GradientForm = void(FunctionType::*)(const arma::mat&, arma::mat&);
//! This is the form of a static Evaluate() method.
template<typename FunctionType>
using EvaluateStaticForm = typename BaseMatType::elem_type(*)(
const BaseMatType&);
//! This is the form of a const Gradient() method.
template<typename FunctionType>
using GradientConstForm =
void(FunctionType::*)(const arma::mat&, arma::mat&) const;
//! This is the form of a non-const Gradient() method.
template<typename FunctionType>
using GradientForm = void(FunctionType::*)(const BaseMatType&, BaseGradType&);
//! This is the form of a static Gradient() method.
template<typename FunctionType>
using GradientStaticForm = void(*)(const arma::mat&, arma::mat&);
//! This is the form of a const Gradient() method.
template<typename FunctionType>
using GradientConstForm =
void(FunctionType::*)(const BaseMatType&, BaseGradType&) const;
//! This is the form of a non-const EvaluateWithGradient() method.
template<typename FunctionType>
using EvaluateWithGradientForm =
double(FunctionType::*)(const arma::mat&, arma::mat&);
//! This is the form of a static Gradient() method.
template<typename FunctionType>
using GradientStaticForm = void(*)(const BaseMatType&, BaseGradType&);
//! This is the form of a const EvaluateWithGradient() method.
template<typename FunctionType>
using EvaluateWithGradientConstForm =
double(FunctionType::*)(const arma::mat&, arma::mat&) const;
//! This is the form of a non-const EvaluateWithGradient() method.
template<typename FunctionType>
using EvaluateWithGradientForm =
typename BaseMatType::elem_type(FunctionType::*)(const BaseMatType&,
BaseGradType&);
//! This is the form of a static EvaluateWithGradient() method.
template<typename FunctionType>
using EvaluateWithGradientStaticForm =
double(*)(const arma::mat&, arma::mat&);
//! This is the form of a const EvaluateWithGradient() method.
template<typename FunctionType>
using EvaluateWithGradientConstForm =
typename BaseMatType::elem_type(FunctionType::*)(const BaseMatType&,
BaseGradType&) const;
//! This is the form of a non-const NumFunctions() method.
template <typename FunctionType>
using NumFunctionsForm = size_t(FunctionType::*)();
//! This is the form of a static EvaluateWithGradient() method.
template<typename FunctionType>
using EvaluateWithGradientStaticForm = typename BaseMatType::elem_type(*)(
const BaseMatType&, BaseGradType&);
//! This is the form of a const NumFunctions() method.
template <typename FunctionType>
using NumFunctionsConstForm = size_t(FunctionType::*)() const;
//! This is the form of a non-const NumFunctions() method.
template <typename FunctionType>
using NumFunctionsForm = size_t(FunctionType::*)();
//! This is the form of a static NumFunctions() method.
template<typename FunctionType>
using NumFunctionsStaticForm = size_t(*)();
//! This is the form of a const NumFunctions() method.
template <typename FunctionType>
using NumFunctionsConstForm = size_t(FunctionType::*)() const;
//! This is the form of a non-const Shuffle() method.
template<typename FunctionType>
using ShuffleForm = void(FunctionType::*)();
//! This is the form of a static NumFunctions() method.
template<typename FunctionType>
using NumFunctionsStaticForm = size_t(*)();
//! This is the form of a const Shuffle() method.
template<typename FunctionType>
using ShuffleConstForm = void(FunctionType::*)() const;
//! This is the form of a non-const Shuffle() method.
template<typename FunctionType>
using ShuffleForm = void(FunctionType::*)();
//! This is the form of a static Shuffle() method.
template<typename FunctionType>
using ShuffleStaticForm = void(*)();
//! This is the form of a const Shuffle() method.
template<typename FunctionType>
using ShuffleConstForm = void(FunctionType::*)() const;
//! This is the form of a decomposable Evaluate() method.
template<typename FunctionType>
using DecomposableEvaluateForm = double(FunctionType::*)(
const arma::mat&, const size_t, const size_t);
//! This is the form of a static Shuffle() method.
template<typename FunctionType>
using ShuffleStaticForm = void(*)();
//! This is the form of a decomposable const Evaluate() method.
template<typename FunctionType>
using DecomposableEvaluateConstForm = double(FunctionType::*)(
const arma::mat&, const size_t, const size_t) const;
//! This is the form of a separable Evaluate() method.
template<typename FunctionType>
using SeparableEvaluateForm =
typename BaseMatType::elem_type(FunctionType::*)(const BaseMatType&,
const size_t,
const size_t);
//! This is the form of a decomposable static Evaluate() method.
template<typename FunctionType>
using DecomposableEvaluateStaticForm = double(*)(
const arma::mat&, const size_t, const size_t);
//! This is the form of a separable const Evaluate() method.
template<typename FunctionType>
using SeparableEvaluateConstForm =
typename BaseMatType::elem_type(FunctionType::*)(const BaseMatType&,
const size_t,
const size_t) const;
//! This is the form of a decomposable non-const Gradient() method.
template<typename FunctionType>
using DecomposableGradientForm = void(FunctionType::*)(
const arma::mat&, const size_t, arma::mat&, const size_t);
//! This is the form of a separable static Evaluate() method.
template<typename FunctionType>
using SeparableEvaluateStaticForm = typename BaseMatType::elem_type(*)(
const BaseMatType&, const size_t, const size_t);
//! This the form of a decomposable const Gradient() method.
template<typename FunctionType>
using DecomposableGradientConstForm = void(FunctionType::*)(
const arma::mat&, const size_t, arma::mat&, const size_t) const;
//! This is the form of a separable non-const Gradient() method.
template<typename FunctionType>
using SeparableGradientForm = void(FunctionType::*)(
const BaseMatType&, const size_t, BaseGradType&, const size_t);
//! This is the form of a decomposable static Gradient() method.
template<typename FunctionType>
using DecomposableGradientStaticForm = void(*)(
const arma::mat&, const size_t, arma::mat&, const size_t);
//! This the form of a separable const Gradient() method.
template<typename FunctionType>
using SeparableGradientConstForm = void(FunctionType::*)(
const BaseMatType&, const size_t, BaseGradType&, const size_t) const;
//! This is the form of a decomposable non-const EvaluateWithGradient() method.
template<typename FunctionType>
using DecomposableEvaluateWithGradientForm = double(FunctionType::*)(
const arma::mat&, const size_t, arma::mat&, const size_t);
//! This is the form of a separable static Gradient() method.
template<typename FunctionType>
using SeparableGradientStaticForm = void(*)(
const BaseMatType&, const size_t, BaseGradType&, const size_t);
//! This is the form of a decomposable const EvaluateWithGradient() method.
template<typename FunctionType>
using DecomposableEvaluateWithGradientConstForm = double(FunctionType::*)(
const arma::mat&, const size_t, arma::mat&, const size_t) const;
//! This is the form of a separable non-const EvaluateWithGradient()
//! method.
template<typename FunctionType>
using SeparableEvaluateWithGradientForm =
typename BaseMatType::elem_type(FunctionType::*)(const BaseMatType&,
const size_t,
BaseGradType&,
const size_t);
//! This is the form of a decomposable static EvaluateWithGradient() method.
template<typename FunctionType>
using DecomposableEvaluateWithGradientStaticForm = double(*)(
const arma::mat&, const size_t, arma::mat&, const size_t);
//! This is the form of a separable const EvaluateWithGradient() method.
template<typename FunctionType>
using SeparableEvaluateWithGradientConstForm =
typename BaseMatType::elem_type(FunctionType::*)(const BaseMatType&,
const size_t,
BaseGradType&,
const size_t) const;
//! This is the form of a non-const NumConstraints() method.
template<typename FunctionType>
using NumConstraintsForm = size_t(FunctionType::*)();
//! This is the form of a separable static EvaluateWithGradient() method.
template<typename FunctionType>
using SeparableEvaluateWithGradientStaticForm =
typename BaseMatType::elem_type(*)(const BaseMatType&,
const size_t,
BaseGradType&,
const size_t);
//! This is the form of a const NumConstraints() method.
template<typename FunctionType>
using NumConstraintsConstForm = size_t(FunctionType::*)() const;
//! This is the form of a non-const NumConstraints() method.
template<typename FunctionType>
using NumConstraintsForm = size_t(FunctionType::*)();
//! This is the form of a static NumConstraints() method.
template<typename FunctionType>
using NumConstraintsStaticForm = size_t(*)();
//! This is the form of a const NumConstraints() method.
template<typename FunctionType>
using NumConstraintsConstForm = size_t(FunctionType::*)() const;
//! This is the form of a non-const EvaluateConstraint() method.
template <typename FunctionType>
using EvaluateConstraintForm = double(FunctionType::*)(
const size_t, const arma::mat&);
//! This is the form of a static NumConstraints() method.
template<typename FunctionType>
using NumConstraintsStaticForm = size_t(*)();
//! This is the form of a const EvaluateConstraint() method.
template<typename FunctionType>
using EvaluateConstraintConstForm = double(FunctionType::*)(
const size_t, const arma::mat&) const;
//! This is the form of a non-const EvaluateConstraint() method.
template <typename FunctionType>
using EvaluateConstraintForm =
typename BaseMatType::elem_type(FunctionType::*)(const size_t,
const BaseMatType&);
//! This is the form of a static EvaluateConstraint() method.
template<typename FunctionType>
using EvaluateConstraintStaticForm = double(*)(const size_t, const arma::mat&);
//! This is the form of a const EvaluateConstraint() method.
template<typename FunctionType>
using EvaluateConstraintConstForm =
typename BaseMatType::elem_type(FunctionType::*)(const size_t,
const BaseMatType&)
const;
//! This is the form of a non-const GradientConstraint() method.
template <typename FunctionType>
using GradientConstraintForm = void(FunctionType::*)(
const size_t, const arma::mat&, arma::mat&);
//! This is the form of a static EvaluateConstraint() method.
template<typename FunctionType>
using EvaluateConstraintStaticForm = typename BaseMatType::elem_type(*)(
const size_t, const BaseMatType&);
//! This is the form of a const GradientConstraint() method.
template<typename FunctionType>
using GradientConstraintConstForm = void(FunctionType::*)(
const size_t, const arma::mat&, arma::mat&) const;
//! This is the form of a non-const GradientConstraint() method.
template <typename FunctionType>
using GradientConstraintForm = void(FunctionType::*)(
const size_t, const BaseMatType&, BaseGradType&);
//! This is the form of a static GradientConstraint() method.
template<typename Class, typename... Ts>
using GradientConstraintStaticForm = void(*)(
const size_t, const arma::mat&, arma::mat&);
//! This is the form of a const GradientConstraint() method.
template<typename FunctionType>
using GradientConstraintConstForm = void(FunctionType::*)(
const size_t, const BaseMatType&, BaseGradType&) const;
//! This is the form of a non-const sparse Gradient() method.
template<typename FunctionType>
using SparseGradientForm = void(FunctionType::*)(
const arma::mat&, const size_t, arma::sp_mat&, const size_t);
//! This is the form of a static GradientConstraint() method.
template<typename Class, typename... Ts>
using GradientConstraintStaticForm = void(*)(
const size_t, const BaseMatType&, BaseGradType&);
//! This is the form of a const sparse Gradient() method.
template<typename FunctionType>
using SparseGradientConstForm = void(FunctionType::*)(
const arma::mat&, const size_t, arma::sp_mat&, const size_t) const;
//! This is the form of a non-const sparse Gradient() method.
//! This check isn't particularly useful---the user needs to specify a sparse
//! gradient type...
template<typename FunctionType>
using SparseGradientForm = void(FunctionType::*)(
const BaseMatType&, const size_t, BaseGradType&, const size_t);
//! This is the form of a static sparse Gradient() method.
template<typename FunctionType>
using SparseGradientStaticForm = void(*)(
const arma::mat&, const size_t, arma::sp_mat&, const size_t);
//! This is the form of a const sparse Gradient() method.
//! This check isn't particularly useful---the user needs to specify a sparse
//! gradient type...
template<typename FunctionType>
using SparseGradientConstForm = void(FunctionType::*)(
const BaseMatType&, const size_t, BaseGradType&, const size_t) const;
//! This is the form of a non-const NumFeatures() method.
template<typename FunctionType>
using NumFeaturesForm = size_t(FunctionType::*)();
//! This is the form of a static sparse Gradient() method.
//! This check isn't particularly useful---the user needs to specify a sparse
//! gradient type...
template<typename FunctionType>
using SparseGradientStaticForm = void(*)(
const BaseMatType&, const size_t, BaseGradType&, const size_t);
//! This is the form of a const NumFeatures() method.
template<typename FunctionType>
using NumFeaturesConstForm = size_t(FunctionType::*)() const;
//! This is the form of a non-const NumFeatures() method.
template<typename FunctionType>
using NumFeaturesForm = size_t(FunctionType::*)();
//! This is the form of a static NumFeatures() method.
template<typename FunctionType>
using NumFeaturesStaticForm = size_t(*)();
//! This is the form of a const NumFeatures() method.
template<typename FunctionType>
using NumFeaturesConstForm = size_t(FunctionType::*)() const;
//! This is the form of a non-const PartialGradient() method.
template<typename FunctionType>
using PartialGradientForm = void(FunctionType::*)(
const arma::mat&, const size_t, arma::sp_mat&);
//! This is the form of a static NumFeatures() method.
template<typename FunctionType>
using NumFeaturesStaticForm = size_t(*)();
//! This is the form of a const PartialGradient() method.
template<typename FunctionType>
using PartialGradientConstForm = void(FunctionType::*)(
const arma::mat&, const size_t, arma::sp_mat&) const;
//! This is the form of a non-const PartialGradient() method.
template<typename FunctionType>
using PartialGradientForm = void(FunctionType::*)(
const BaseMatType&, const size_t, BaseGradType&);
//! This is the form of a static PartialGradient() method.
template<typename FunctionType>
using PartialGradientStaticForm = void(*)(
const arma::mat&, const size_t, arma::sp_mat&);
//! This is the form of a const PartialGradient() method.
template<typename FunctionType>
using PartialGradientConstForm = void(FunctionType::*)(
const BaseMatType&, const size_t, BaseGradType&) const;
//! This is a utility struct that will match any non-const form.
template<typename FunctionType, typename... Ts>
using OtherForm = double(FunctionType::*)(Ts...);
//! This is the form of a static PartialGradient() method.
template<typename FunctionType>
using PartialGradientStaticForm = void(*)(
const BaseMatType&, const size_t, BaseGradType&);
//! This is a utility struct that will match any const form.
template<typename FunctionType, typename... Ts>
using OtherConstForm = double(FunctionType::*)(Ts...) const;
//! This is a utility struct that will match any non-const form.
template<typename FunctionType, typename... Ts>
using OtherForm = typename BaseMatType::elem_type(FunctionType::*)(Ts...);
//! This is a utility struct that will match any static form.
template<typename FunctionType, typename... Ts>
using OtherStaticForm = double(*)(Ts...);
//! This is a utility struct that will match any const form.
template<typename FunctionType, typename... Ts>
using OtherConstForm = typename BaseMatType::elem_type(FunctionType::*)(Ts...)
const;
//! This is a utility struct that will match any static form.
template<typename FunctionType, typename... Ts>
using OtherStaticForm = typename BaseMatType::elem_type(*)(Ts...);
};
/**
* This is a utility type used to provide unusable overloads from each of the
@@ -299,12 +342,12 @@ struct HasNonConstSignatures
/**
* Utility struct: sometimes we want to know if we have two functions available,
* and that at least one of them is const and both of them are not non-const and
* non-static. If the corresponding checkers (from ENS_HAS_METHOD_FORM()) are given
* as CheckerA and CheckerB, and the corresponding const and static function
* signatures are given as ConstSignatureA, StaticSignatureA, ConstSignatureB,
* and StaticSignatureB, then 'value' will be true if methods with the correct
* names exist in the given ClassType and at least one of those two methods is
* const, and neither method is non-const and non-static.
* non-static. If the corresponding checkers (from ENS_HAS_METHOD_FORM()) are
* given as CheckerA and CheckerB, and the corresponding const and static
* function signatures are given as ConstSignatureA, StaticSignatureA,
* ConstSignatureB, and StaticSignatureB, then 'value' will be true if methods
* with the correct names exist in the given ClassType and at least one of those
* two methods is const, and neither method is non-const and non-static.
*/
template<typename ClassType,
template<typename, template<typename...> class, size_t> class CheckerA,
@@ -332,6 +375,60 @@ struct HasConstSignatures
const static bool value = HasEitherConstForm && HasAnyFormA && HasAnyFormB;
};
//! Utility struct, check if size_t BatchSize() const or size_t BatchSize()
//! exists.
template<typename OptimizerType>
struct HasBatchSizeSignature
{
template<typename C>
using BatchSizeConstForm = size_t(C::*)(void) const;
template<typename C>
using BatchSizeForm = size_t(C::*)(void);
const static bool value =
HasBatchSize<OptimizerType, BatchSizeForm>::value ||
HasBatchSize<OptimizerType, BatchSizeConstForm>::value;
};
//! Utility struct, check if size_t MaxIterations() const exists.
template<typename OptimizerType>
struct HasMaxIterationsSignature
{
template<typename C>
using HasMaxIterationsForm = size_t(C::*)(void) const;
const static bool value =
HasMaxIterations<OptimizerType, HasMaxIterationsForm>::value;
};
//! Utility struct, check if size_t NumFunctions() const or
//! size_t NumFunctions() exists.
template<typename OptimizerType>
struct HasNumFunctionsSignature
{
template<typename C>
using NumFunctionsConstForm = size_t(C::*)(void) const;
template<typename C>
using NumFunctionsForm = size_t(C::*)(void);
const static bool value =
HasNumFunctions<OptimizerType, NumFunctionsForm>::value ||
HasNumFunctions<OptimizerType, NumFunctionsConstForm>::value;
};
//! Utility struct, check if bool ResetPolicy() exists.
template<typename OptimizerType>
struct HasResetPolicySignature
{
template<typename C>
using HasResetPolicyForm = bool&(C::*)(void);
const static bool value =
HasResetPolicy<OptimizerType, HasResetPolicyForm>::value;
};
} // namespace traits
} // namespace ens
+4 -3
View File
@@ -18,7 +18,8 @@ namespace ens {
/**
* Class to hold the information and operations of current atoms in the
* soluton space.
* soluton space. This is not fully templatized, and may cost some extra
* operations for the conversion.
*/
class Atoms
{
@@ -31,7 +32,7 @@ class Atoms
* @param v new atom to be added.
* @param c coefficient of the new atom.
*/
void AddAtom(const arma::vec& v, FuncSq& function, const double c = 0)
void AddAtom(const arma::mat& v, FuncSq& function, const double c = 0)
{
if (currentAtoms.is_empty())
{
@@ -113,7 +114,7 @@ class Atoms
// add an atom norm constraint, you could use projected gradient method,
// see the implementaton of ProjectedGradientEnhancement().
arma::vec newCoeffs =
solve(function.MatrixA() * newAtoms, function.Vectorb());
solve(function.MatrixA() * newAtoms, function.Vectorb(), arma::solve_opts::fast);
// Evaluate the function again.
double Fnew = function.Evaluate(newAtoms * newCoeffs);
+19 -13
View File
@@ -79,36 +79,42 @@ class ConstrLpBallSolver
* @param v Input local gradient.
* @param s Output optimal solution in the constrained domain (lp ball).
*/
void Optimize(const arma::mat& v,
arma::mat& s)
template<typename MatType>
void Optimize(const MatType& v,
MatType& s)
{
typedef typename MatType::elem_type ElemType;
if (p == std::numeric_limits<double>::infinity())
{
// l-inf ball.
s = -sign(v);
s = -arma::sign(v);
if (regFlag)
s = s / lambda; // element-wise division.
{
// Do element-wise division.
s /= arma::conv_to<arma::Col<ElemType>>::from(lambda);
}
}
else if (p > 1.0)
{
// lp ball with 1<p<inf.
if (regFlag)
s = v / lambda;
s = v / arma::conv_to<arma::Col<ElemType>>::from(lambda);
else
s = v;
double q = 1 / (1.0 - 1.0 / p);
s = - sign(v) % pow(abs(s), q - 1); // element-wise multiplication.
s = -arma::sign(v) % arma::pow(arma::abs(s), q - 1);
s = arma::normalise(s, p);
if (regFlag)
s = s / lambda;
s = s / arma::conv_to<arma::Col<ElemType>>::from(lambda);
}
else if (p == 1.0)
{
// l1 ball, also used in OMP.
if (regFlag)
s = arma::abs(v / lambda);
s = arma::abs(v / arma::conv_to<arma::Col<ElemType>>::from(lambda));
else
s = arma::abs(v);
@@ -119,7 +125,7 @@ class ConstrLpBallSolver
s(k) = -((0.0 < v(k)) - (v(k) < 0.0));
if (regFlag)
s = s / lambda;
s = s / arma::conv_to<arma::Col<ElemType>>::from(lambda);
}
else
{
@@ -135,14 +141,14 @@ class ConstrLpBallSolver
double& P() { return p;}
//! Get regularization flag.
bool RegFlag() const {return regFlag;}
bool RegFlag() const { return regFlag; }
//! Modify regularization flag.
bool& RegFlag() {return regFlag;}
bool& RegFlag() { return regFlag; }
//! Get the regularization parameter.
arma::vec Lambda() const {return lambda;}
arma::vec Lambda() const { return lambda; }
//! Modify the regularization parameter.
arma::vec& Lambda() {return lambda;}
arma::vec& Lambda() { return lambda; }
private:
//! lp norm, 1<=p<=inf;
@@ -73,7 +73,7 @@ class ConstrStructGroupSolver
* group, and compute norm in each group.
*/
ConstrStructGroupSolver(GroupType& groupExtractor) :
groupExtractor(groupExtractor)
groupExtractor(groupExtractor)
{ /* Nothing to do */ }
/**
@@ -82,18 +82,21 @@ class ConstrStructGroupSolver
* @param v Input local gradient.
* @param s Output optimal solution in the constrained atom domain.
*/
void Optimize(const arma::mat& v, arma::mat& s)
template<typename MatType>
void Optimize(const MatType& v, MatType& s)
{
typedef typename MatType::elem_type ElemType;
size_t nGroups = groupExtractor.NumGroups();
double dualNorm = 0;
ElemType dualNorm = 0;
size_t optimalGroup = 1;
// Find the optimal group.
for (size_t i = 1; i <= nGroups; ++i)
{
arma::vec y;
MatType y;
groupExtractor.ProjectToGroup(v, i, y);
double newNorm = groupExtractor.DualNorm(y, i);
ElemType newNorm = groupExtractor.DualNorm(y, i);
// Find the group with largest dual norm.
if (newNorm > dualNorm)
@@ -142,11 +145,12 @@ class GroupLpBall
* @param groupId input ID number of the group, start from 1.
* @param y output projection of the vector to specific group.
*/
void ProjectToGroup(const arma::mat& v, const size_t groupId, arma::vec& y)
template<typename MatType>
void ProjectToGroup(const MatType& v, const size_t groupId, MatType& y)
{
arma::uvec& indList = groupIndicesList[groupId - 1];
size_t dim = indList.n_elem;
y.set_size(dim);
y.set_size(dim, 1);
for (size_t i = 0; i < dim; ++i)
y(i) = v(indList(i));
@@ -160,14 +164,15 @@ class GroupLpBall
* @param groupId optimal atom belongs to this group.
* @param s output optimal atom.
*/
void OptimalFromGroup(const arma::mat& v, const size_t groupId, arma::mat& s)
template<typename MatType>
void OptimalFromGroup(const MatType& v, const size_t groupId, MatType& s)
{
// Project v to group.
arma::vec yk;
MatType yk;
ProjectToGroup(v, groupId, yk);
// Optimize in this group.
arma::vec sProj(yk.n_elem);
MatType sProj(yk.n_elem, 1);
lpBallSolver.Optimize(yk, sProj);
// Recover s to the original dimension.
@@ -190,7 +195,8 @@ class GroupLpBall
* @param yk compute the q-norm of yk.
* @param groupId group ID number.
*/
double DualNorm(const arma::vec& yk, const int groupId)
template<typename MatType>
typename MatType::elem_type DualNorm(const MatType& yk, const int groupId)
{
if (p == std::numeric_limits<double>::infinity())
{
@@ -200,8 +206,8 @@ class GroupLpBall
else if (p > 1.0)
{
// p norm, return q-norm
double q = 1.0 / (1.0 - 1.0/p);
return arma::norm(yk, q);
double q = 1.0 / (1.0 - 1.0 / p);
return arma::norm(yk, q);
}
else if (p == 1.0)
{
@@ -233,9 +239,6 @@ class GroupLpBall
ConstrLpBallSolver lpBallSolver;
};
} // namespace ens
#endif
+25 -2
View File
@@ -114,13 +114,36 @@ class FrankWolfe
* 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>
double Optimize(FunctionType& function, arma::mat& iterate);
template<typename FunctionType, typename MatType, typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<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 linear constrained solver.
const LinearConstrSolverType& LinearConstrSolver()
+40 -13
View File
@@ -39,34 +39,56 @@ FrankWolfe(const LinearConstrSolverType linearConstrSolver,
template<
typename LinearConstrSolverType,
typename UpdateRuleType>
template<typename FunctionType>
double FrankWolfe<LinearConstrSolverType, UpdateRuleType>::
Optimize(FunctionType& function, arma::mat& iterate)
template<typename FunctionType, typename MatType, typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
FrankWolfe<LinearConstrSolverType, UpdateRuleType>::Optimize(
FunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
typedef Function<FunctionType> FullFunctionType;
// 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>();
traits::CheckFunctionTypeAPI<FullFunctionType, BaseMatType, BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
BaseMatType& iterate = (BaseMatType&) iterateIn;
// To keep track of the function value.
double currentObjective = DBL_MAX;
ElemType currentObjective = std::numeric_limits<ElemType>::max();
arma::mat gradient(iterate.n_rows, iterate.n_cols);
arma::mat s(iterate.n_rows, iterate.n_cols);
arma::mat iterateNew(iterate.n_rows, iterate.n_cols);
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
BaseMatType s(iterate.n_rows, iterate.n_cols);
BaseMatType iterateNew(iterate.n_rows, iterate.n_cols);
double gap = 0;
for (size_t i = 1; i != maxIterations; ++i)
// Controls early termination of the optimization process.
bool terminate = false;
terminate |= Callback::BeginOptimization(*this, f, iterate, callbacks...);
for (size_t i = 1; i != maxIterations && !terminate; ++i)
{
currentObjective = f.EvaluateWithGradient(iterate, gradient);
terminate |= Callback::EvaluateWithGradient(*this, f, iterate,
currentObjective, gradient, callbacks...);
// Output current objective function.
Info << "FrankWolfe::Optimize(): iteration " << i << ", objective "
<< currentObjective << "." << std::endl;
// Solve linear constrained problem, solution saved in s.
linearConstrSolver.Optimize(gradient, s);
linearConstrSolver.Optimize(gradient, s, callbacks...);
// Check duality gap for return condition.
gap = std::fabs(dot(iterate - s, gradient));
@@ -74,18 +96,23 @@ Optimize(FunctionType& function, arma::mat& iterate)
{
Info << "FrankWolfe::Optimize(): minimized within tolerance "
<< tolerance << "; " << "terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return currentObjective;
}
// Update solution, save in iterateNew.
updateRule.Update(f, iterate, s, iterateNew, i);
updateRule.template Update<FunctionType, BaseMatType, BaseGradType>(f,
iterate, s, iterateNew, i);
iterate = std::move(iterateNew);
terminate |= Callback::StepTaken(*this, f, iterate, callbacks...);
}
Info << "FrankWolfe::Optimize(): maximum iterations (" << maxIterations
<< ") reached; " << "terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return currentObjective;
} // Optimize()
@@ -44,8 +44,12 @@ class LineSearch
* coordinate of the optimal solution.
* @return Minimum solution function value.
*/
template<typename FunctionType>
double Optimize(FunctionType& function, const arma::mat& x1, arma::mat& x2);
template<typename FunctionType,
typename MatType,
typename GradType = MatType>
typename MatType::elem_type Optimize(FunctionType& function,
const MatType& x1,
MatType& x2);
//! Get the maximum number of iterations (0 indicates no limit).
size_t MaxIterations() const { return maxIterations; }
@@ -74,11 +78,11 @@ class LineSearch
*
* @return Derivative of function(x0 + gamma * deltaX) with respect to gamma.
*/
template<typename FunctionType>
double Derivative(FunctionType& function,
const arma::mat& x0,
const arma::mat& deltaX,
const double gamma);
template<typename FunctionType, typename MatType, typename GradType>
typename MatType::elem_type Derivative(FunctionType& function,
const MatType& x0,
const MatType& deltaX,
const double gamma);
}; // class LineSearch
} // namespace ens
@@ -19,24 +19,28 @@
namespace ens {
template<typename FunctionType>
double LineSearch::Optimize(FunctionType& function,
const arma::mat& x1,
arma::mat& x2)
template<typename FunctionType, typename MatType, typename GradType>
typename MatType::elem_type LineSearch::Optimize(FunctionType& function,
const MatType& x1,
MatType& x2)
{
typedef Function<FunctionType> FullFunctionType;
typedef typename MatType::elem_type ElemType;
typedef Function<FunctionType, MatType, GradType> FullFunctionType;
FullFunctionType& f = static_cast<FullFunctionType&>(function);
// Check that we have all the functions we will need.
traits::CheckFunctionTypeAPI<FullFunctionType>();
traits::CheckFunctionTypeAPI<FullFunctionType, MatType, GradType>();
// Set up the search line, that is,
// find the zero of der(gamma) = Derivative(gamma).
arma::mat deltaX = x2 - x1;
double gamma = 0;
double derivative = Derivative(f, x1, deltaX, 0);
double derivativeNew = Derivative(f, x1, deltaX, 1);
double secant = derivativeNew - derivative;
MatType deltaX = x2 - x1;
ElemType gamma = 0;
ElemType derivative = Derivative<FunctionType, MatType, GradType>(f, x1,
deltaX, 0);
ElemType derivativeNew = Derivative<FunctionType, MatType, GradType>(f, x1,
deltaX, 1);
ElemType secant = derivativeNew - derivative;
if (derivative >= 0.0) // Optimal solution at left endpoint.
{
@@ -65,12 +69,13 @@ double LineSearch::Optimize(FunctionType& function,
}
// Solve new gamma.
double gammaNew = gamma - derivative / secant;
gammaNew = std::max(gammaNew, 0.0);
gammaNew = std::min(gammaNew, 1.0);
ElemType gammaNew = gamma - derivative / secant;
gammaNew = std::max(gammaNew, ElemType(0.0));
gammaNew = std::min(gammaNew, ElemType(1.0));
// Update secant, gamma and derivative
derivativeNew = Derivative(function, x1, deltaX, gammaNew);
// Update secant, gamma and derivative.
derivativeNew = Derivative<FunctionType, MatType, GradType>(function, x1,
deltaX, gammaNew);
secant = (derivativeNew - derivative) / (gammaNew - gamma);
gamma = gammaNew;
derivative = derivativeNew;
@@ -93,17 +98,17 @@ double LineSearch::Optimize(FunctionType& function,
//! Derivative of the function along the search line.
template<typename FunctionType>
double LineSearch::Derivative(FunctionType& function,
const arma::mat& x0,
const arma::mat& deltaX,
const double gamma)
template<typename FunctionType, typename MatType, typename GradType>
typename MatType::elem_type LineSearch::Derivative(FunctionType& function,
const MatType& x0,
const MatType& deltaX,
const double gamma)
{
arma::mat gradient(x0.n_rows, x0.n_cols);
GradType gradient(x0.n_rows, x0.n_cols);
function.Gradient(x0 + gamma * deltaX, gradient);
return arma::dot(gradient, deltaX);
}
} // namespace ens
#endif
@@ -35,7 +35,8 @@ class Proximal
* also saved in v.
* @param tau Norm of l1 ball.
*/
static void ProjectToL1Ball(arma::vec& v, double tau);
template<typename MatType>
static void ProjectToL1Ball(MatType& v, double tau);
/**
* Project the vector onto the l0 ball with norm tau. That is, we try to
@@ -48,7 +49,8 @@ class Proximal
* also saved in v.
* @param tau Norm of l0 ball.
*/
static void ProjectToL0Ball(arma::vec& v, int tau);
template<typename MatType>
static void ProjectToL0Ball(MatType& v, int tau);
}; // class Proximal
} // namespace ens
@@ -32,30 +32,31 @@ namespace ens {
*
* This is just a soft thresholding.
*/
inline void Proximal::ProjectToL1Ball(arma::vec& v, double tau)
template<typename MatType>
inline void Proximal::ProjectToL1Ball(MatType& v, double tau)
{
arma::vec simplexSol = arma::abs(v);
MatType simplexSol = arma::abs(v);
// Already with L1 norm <= tau.
if (arma::accu(simplexSol) <= tau)
return;
simplexSol = arma::sort(simplexSol, "descend");
arma::vec simplexSum = arma::cumsum(simplexSol);
MatType simplexSum = arma::cumsum(simplexSol);
double nu = 0;
size_t rho;
size_t rho = 0;
for (size_t j = 1; j <= simplexSol.n_rows; j++)
{
rho = simplexSol.n_rows - j;
nu = simplexSol(rho) - (simplexSum(rho) - tau)/(rho + 1);
nu = simplexSol(rho) - (simplexSum(rho) - tau) / (rho + 1);
if (nu > 0)
break;
}
double theta = (simplexSum(rho) - tau)/rho;
double theta = (simplexSum(rho) - tau) / rho;
// Threshold on absolute value of v with theta.
for (arma::uword j = 0; j< simplexSol.n_rows; j++)
for (arma::uword j = 0; j < simplexSol.n_rows; j++)
{
if (v(j) >= 0.0)
v(j) = std::max(v(j) - theta, 0.0);
@@ -68,7 +69,8 @@ inline void Proximal::ProjectToL1Ball(arma::vec& v, double tau)
* Approximate the vector v with a tau-sparse vector.
* This is a hard-thresholding.
*/
inline void Proximal::ProjectToL0Ball(arma::vec& v, int tau)
template<typename MatType>
inline void Proximal::ProjectToL0Ball(MatType& v, int tau)
{
arma::uvec indices = arma::sort_index(arma::abs(v));
arma::uword numberToKill = v.n_elem - tau;
+10 -10
View File
@@ -36,20 +36,20 @@ class UpdateClassic
*
* \f$ x_{k+1} = (1-\gamma)x_k + \gamma s \f$, where \f$ \gamma = 2/(k+2) \f$
*
* @param function function to be optimized, not used in this update rule.
* @param oldCoords previous solution coords.
* @param s current linear_constr_solution result.
* @param newCoords output new solution coords.
* @param numIter current iteration number
* @param function Function to be optimized, not used in this update rule.
* @param oldCoords Previous solution coords.
* @param s Current linear_constr_solution result.
* @param newCoords Output new solution coords.
* @param numIter Current iteration number.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
void Update(FunctionType& /* function */,
const arma::mat& oldCoords,
const arma::mat& s,
arma::mat& newCoords,
const MatType& oldCoords,
const MatType& s,
MatType& newCoords,
const size_t numIter)
{
double gamma = 2.0 / (numIter + 2.0);
typename MatType::elem_type gamma = 2.0 / (numIter + 2.0);
newCoords = (1.0 - gamma) * oldCoords + gamma * s;
}
};
@@ -49,31 +49,36 @@ class UpdateFullCorrection
* Update rule for FrankWolfe, recalculate the coefficents of of current
* atoms, while satisfying the norm constraint.
*
* @param function function to be optimized.
* @param oldCoords previous solution coords.
* @param s current linear_constr_solution result.
* @param newCoords new output solution coords.
* @param numIter current iteration number.
* FuncSqType is an ignored type to match the requirements of the class.
*
* @param function Function to be optimized.
* @param oldCoords Previous solution coords.
* @param s Current linear_constr_solution result.
* @param newCoords New output solution coords.
* @param numIter Current iteration number.
*/
template<typename FuncSqType, typename MatType, typename GradType>
void Update(FuncSq& function,
const arma::mat& oldCoords,
const arma::mat& s,
arma::mat& newCoords,
const MatType& oldCoords,
const MatType& s,
MatType& newCoords,
const size_t /* numIter */)
{
// Line search, with explicit solution here.
arma::mat v = tau * s - oldCoords;
arma::mat b = function.Vectorb();
arma::mat A = function.MatrixA();
double gamma = arma::dot(b - A * oldCoords, A * v);
MatType v = tau * s - oldCoords;
MatType b = function.Vectorb();
MatType A = function.MatrixA();
typename MatType::elem_type gamma = arma::dot(b - A * oldCoords, A * v);
gamma = gamma / std::pow(arma::norm(A * v, "fro"), 2);
gamma = std::min(gamma, 1.0);
atoms.CurrentCoeffs() = (1.0 - gamma) * atoms.CurrentCoeffs();
atoms.AddAtom(s, function, gamma * tau);
atoms.AddAtom(arma::mat(s), function, gamma * tau);
// Projected gradient method for enhancement.
atoms.ProjectedGradientEnhancement(function, tau, stepSize);
atoms.RecoverVector(newCoords);
arma::mat tmp;
atoms.RecoverVector(tmp);
newCoords = arma::conv_to<MatType>::from(tmp);
}
private:
@@ -40,7 +40,6 @@ class UpdateLineSearch
tolerance(tolerance), maxIterations(maxIterations)
{/* Do nothing */}
/**
* Update rule for FrankWolfe, optimize with line search using secant method.
*
@@ -64,18 +63,19 @@ class UpdateLineSearch
* @param newCoords output new solution coords.
* @param numIter current iteration number, not used here.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType, typename GradType>
void Update(FunctionType& function,
const arma::mat& oldCoords,
const arma::mat& s,
arma::mat& newCoords,
const MatType& oldCoords,
const MatType& s,
MatType& newCoords,
const size_t /* numIter */)
{
LineSearch solver(maxIterations, tolerance);
newCoords = s;
solver.Optimize(function, oldCoords, newCoords);
solver.Optimize<FunctionType, MatType, GradType>(function, oldCoords,
newCoords);
}
//! Get the tolerance for termination.
+11 -7
View File
@@ -45,22 +45,25 @@ class UpdateSpan
* @param newCoords output new solution coords.
* @param numIter current iteration number.
*/
template<typename FuncSqType, typename MatType, typename GradType>
void Update(FuncSq& function,
const arma::mat& oldCoords,
const arma::mat& s,
arma::mat& newCoords,
const MatType& oldCoords,
const MatType& s,
MatType& newCoords,
const size_t /* numIter */)
{
// Add new atom into soluton space.
atoms.AddAtom(s, function);
atoms.AddAtom(arma::mat(s), function);
// Reoptimize the solution in the current space.
arma::vec b = function.Vectorb();
atoms.CurrentCoeffs() = solve(function.MatrixA() * atoms.CurrentAtoms(), b);
atoms.CurrentCoeffs() = solve(function.MatrixA() * atoms.CurrentAtoms(), b, arma::solve_opts::fast);
// x has coords of only the current atoms, recover the solution
// to the original size.
atoms.RecoverVector(newCoords);
arma::mat tmp;
atoms.RecoverVector(tmp);
newCoords = arma::conv_to<MatType>::from(tmp);
// Prune the support.
if (isPrune)
@@ -68,7 +71,8 @@ class UpdateSpan
double oldF = function.Evaluate(oldCoords);
double F = 0.25 * oldF + 0.75 * function.Evaluate(newCoords);
atoms.PruneSupport(F, function);
atoms.RecoverVector(newCoords);
atoms.RecoverVector(tmp);
newCoords = arma::conv_to<MatType>::from(tmp);
}
}
@@ -65,12 +65,36 @@ class GradientDescent
* the final objective value is returned.
*
* @tparam FunctionType 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 FunctionType>
double Optimize(FunctionType& function, arma::mat& iterate);
template<typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(FunctionType& function,
MatType& iterate,
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)...);
}
/**
* Assert all dimensions are numeric and optimize the given function using
@@ -82,19 +106,44 @@ class GradientDescent
* tuning module.
*
* @tparam FunctionType 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 categoricalDimensions A vector of dimension information. If a value
* is true, then that dimension is a categorical dimension.
* @param numCategories Number of categories in each categorical dimension.
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename FunctionType>
double Optimize(
FunctionType& function,
arma::mat& iterate,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories);
template<typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(FunctionType& function,
MatType& iterate,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories,
CallbackTypes&&... callbacks);
//! Forward the MatType as GradType.
template<typename FunctionType,
typename MatType,
typename... CallbackTypes>
typename MatType::elem_type Optimize(
FunctionType& function,
MatType& iterate,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories,
CallbackTypes&&... callbacks)
{
return Optimize<FunctionType, MatType, MatType,
CallbackTypes...>(function, iterate, categoricalDimensions,
numCategories, std::forward<CallbackTypes>(callbacks)...);
}
//! Get the step size.
double StepSize() const { return stepSize; }
@@ -30,27 +30,50 @@ inline GradientDescent::GradientDescent(
{ /* Nothing to do. */ }
//! Optimize the function (minimize).
template<typename FunctionType>
double GradientDescent::Optimize(
FunctionType& function, arma::mat& iterate)
template<typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
GradientDescent::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;
// Use the Function<> wrapper type to provide additional functionality.
typedef Function<FunctionType> FullFunctionType;
typedef Function<FunctionType, BaseMatType, BaseGradType> FullFunctionType;
FullFunctionType& f(static_cast<FullFunctionType&>(function));
// Make sure we have the methods that we need.
traits::CheckFunctionTypeAPI<FullFunctionType>();
traits::CheckFunctionTypeAPI<FullFunctionType, BaseMatType, BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
// To keep track of where we are and how things are going.
double overallObjective = std::numeric_limits<double>::max();
double lastObjective = std::numeric_limits<double>::max();
ElemType overallObjective = std::numeric_limits<ElemType>::max();
ElemType lastObjective = std::numeric_limits<ElemType>::max();
BaseMatType& iterate = (BaseMatType&) iterateIn;
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
// Controls early termination of the optimization process.
bool terminate = false;
// Now iterate!
arma::mat gradient(iterate.n_rows, iterate.n_cols);
for (size_t i = 1; i != maxIterations; ++i)
terminate |= Callback::BeginOptimization(*this, f, iterate, callbacks...);
for (size_t i = 1; i != maxIterations && !terminate; ++i)
{
overallObjective = f.EvaluateWithGradient(iterate, gradient);
terminate |= Callback::EvaluateWithGradient(*this, f, iterate,
overallObjective, gradient, callbacks...);
// Output current objective function.
Info << "Gradient Descent: iteration " << i << ", objective "
<< overallObjective << "." << std::endl;
@@ -60,6 +83,8 @@ double GradientDescent::Optimize(
Warn << "Gradient Descent: converged to " << overallObjective
<< "; terminating" << " with failure. Try a smaller step size?"
<< std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
@@ -67,6 +92,8 @@ double GradientDescent::Optimize(
{
Info << "Gradient Descent: minimized within tolerance "
<< tolerance << "; " << "terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
@@ -75,19 +102,28 @@ double GradientDescent::Optimize(
// And update the iterate.
iterate -= stepSize * gradient;
terminate |= Callback::StepTaken(*this, f, iterate, callbacks...);
}
Info << "Gradient Descent: maximum iterations (" << maxIterations
<< ") reached; " << "terminating optimization." << std::endl;
Callback::EndOptimization(*this, f, iterate, callbacks...);
return overallObjective;
}
template<typename FunctionType>
double GradientDescent::Optimize(
template<typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
GradientDescent::Optimize(
FunctionType& function,
arma::mat& iterate,
MatType& iterate,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories)
const arma::Row<size_t>& numCategories,
CallbackTypes&&... callbacks)
{
if (categoricalDimensions.size() != iterate.n_rows)
{
@@ -118,7 +154,7 @@ double GradientDescent::Optimize(
}
}
return Optimize(function, iterate);
return Optimize(function, iterate, callbacks...);
}
} // namespace ens
@@ -30,6 +30,8 @@ class GridSearch
* possible combinations of values for the parameters specified in
* datasetInfo.
*
* @tparam FunctionType Type of function to optimize.
* @tparam MatType Type of matrix to optimize with.
* @param function Function to optimize.
* @param bestParameters Variable for storing results.
* @param categoricalDimensions Set of dimension types. If a value is true,
@@ -37,10 +39,10 @@ class GridSearch
* @param numCategories Number of categories in each categorical dimension.
* @return Objective value of the final point.
*/
template<typename FunctionType>
double Optimize(
template<typename FunctionType, typename MatType>
typename MatType::elem_type Optimize(
FunctionType& function,
arma::mat& bestParameters,
MatType& bestParameters,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories);
@@ -52,12 +54,12 @@ class GridSearch
* (parameters) are specified in the first i rows of the currentParameters
* argument.
*/
template<typename FunctionType>
template<typename FunctionType, typename MatType>
void Optimize(
FunctionType& function,
double& bestObjective,
arma::mat& bestParameters,
arma::vec& currentParameters,
typename MatType::elem_type& bestObjective,
MatType& bestParameters,
MatType& currentParameters,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories,
size_t i);
@@ -17,10 +17,10 @@
namespace ens {
template<typename FunctionType>
double GridSearch::Optimize(
template<typename FunctionType, typename MatType>
typename MatType::elem_type GridSearch::Optimize(
FunctionType& function,
arma::mat& bestParameters,
MatType& bestParameters,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories)
{
@@ -35,9 +35,12 @@ double GridSearch::Optimize(
}
}
double bestObjective = std::numeric_limits<double>::max();
bestParameters = arma::mat(categoricalDimensions.size(), 1);
arma::vec currentParameters = arma::vec(categoricalDimensions.size());
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
ElemType bestObjective = std::numeric_limits<ElemType>::max();
bestParameters.set_size(categoricalDimensions.size(), 1);
MatType currentParameters(categoricalDimensions.size(), 1);
/* Initialize best parameters for the case (very unlikely though) when no set
* of parameters gives an objective value better than
@@ -51,18 +54,23 @@ double GridSearch::Optimize(
return bestObjective;
}
template<typename FunctionType>
template<typename FunctionType, typename MatType>
void GridSearch::Optimize(
FunctionType& function,
double& bestObjective,
arma::mat& bestParameters,
arma::vec& currentParameters,
typename MatType::elem_type& bestObjective,
MatType& bestParameters,
MatType& currentParameters,
const std::vector<bool>& categoricalDimensions,
const arma::Row<size_t>& numCategories,
size_t i)
{
// Make sure we have the methods that we need.
traits::CheckNonDifferentiableFunctionTypeAPI<FunctionType>();
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
// Make sure we have the methods that we need. No restrictions on the matrix
// type are needed.
traits::CheckArbitraryFunctionTypeAPI<FunctionType, BaseMatType>();
if (i < categoricalDimensions.size())
{
@@ -75,7 +83,7 @@ void GridSearch::Optimize(
}
else
{
double objective = function.Evaluate(currentParameters);
ElemType objective = function.Evaluate((BaseMatType&) currentParameters);
if (objective < bestObjective)
{
bestObjective = objective;
+27 -3
View File
@@ -74,13 +74,37 @@ class IQN
* modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate);
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
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 stepSize; }
+93 -40
View File
@@ -32,10 +32,34 @@ inline IQN::IQN(const double stepSize,
{ /* Nothing to do. */ }
//! Optimize the function (minimize).
template<typename DecomposableFunctionType>
double IQN::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
IQN::Optimize(SeparableFunctionType& functionIn,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
traits::CheckDecomposableFunctionTypeAPI<DecomposableFunctionType>();
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
typedef Function<SeparableFunctionType, BaseMatType, BaseGradType>
FullFunctionType;
FullFunctionType& function(static_cast<FullFunctionType&>(functionIn));
// Make sure we have all the methods that we need.
traits::CheckSeparableFunctionTypeAPI<FullFunctionType, BaseMatType,
BaseGradType>();
RequireDenseFloatingPointType<BaseMatType>();
RequireDenseFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
traits::CheckSeparableFunctionTypeAPI<SeparableFunctionType,
BaseMatType, BaseGradType>();
// Find the number of functions.
const size_t numFunctions = function.NumFunctions();
@@ -43,42 +67,54 @@ double IQN::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
if (numFunctions % batchSize != 0)
++numBatches; // Capture last few.
BaseMatType& iterate = (BaseMatType&) iterateIn;
// To keep track of where we are and how things are going.
double overallObjective = 0;
ElemType overallObjective = 0;
arma::cube y(iterate.n_rows, iterate.n_cols, numBatches);
arma::cube t(iterate.n_elem, 1, numBatches);
arma::cube Q(iterate.n_elem, iterate.n_elem, numBatches);
arma::mat initialIterate = arma::randn(iterate.n_rows, iterate.n_cols);
arma::mat B = arma::eye(iterate.n_elem, iterate.n_elem);
// Controls early termination of the optimization process.
bool terminate = false;
arma::mat g = arma::zeros(iterate.n_rows, iterate.n_cols);
std::vector<BaseGradType> y(numBatches, BaseGradType(iterate.n_rows,
iterate.n_cols));
std::vector<BaseMatType> t(numBatches, BaseMatType(iterate.n_rows,
iterate.n_cols));
std::vector<BaseMatType> Q(numBatches, BaseMatType(iterate.n_elem,
iterate.n_elem));
BaseMatType initialIterate = arma::randn<arma::Mat<ElemType>>(iterate.n_rows,
iterate.n_cols);
BaseGradType B(iterate.n_elem, iterate.n_elem);
B.eye();
BaseGradType g(iterate.n_rows, iterate.n_cols);
g.zeros();
for (size_t i = 0, f = 0; i < numFunctions; f++)
{
// Find the effective batch size (the last batch may be smaller).
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - i);
t.slice(f) = arma::mat(initialIterate.memptr(), iterate.n_elem,
1, false, false);
function.Gradient(initialIterate, i, y.slice(f), effectiveBatchSize);
// It would be nice to avoid this copy but it is difficult to be generic to
// any MatType and still do that.
t[f] = initialIterate;
function.Gradient(initialIterate, i, y[f], effectiveBatchSize);
Q.slice(f).eye();
g += y.slice(f);
y.slice(f) /= (double) effectiveBatchSize;
terminate |= Callback::Gradient(*this, function, initialIterate,
y[f], callbacks...);
Q[f].eye();
g += y[f];
y[f] /= (double) effectiveBatchSize;
i += effectiveBatchSize;
}
g /= numFunctions;
arma::mat gradient(iterate.n_rows, iterate.n_cols);
arma::mat u = t.slice(0);
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
BaseMatType u = t[0];
// Convenience alias to avoid multiple use of arma::vectorise.
arma::mat iterateVec = arma::mat(iterate.memptr(), iterate.n_elem,
1, false, false);
arma::mat gVec = arma::mat(g.memptr(), iterate.n_elem, 1, false, false);
for (size_t i = 1; i != maxIterations; ++i)
terminate |= Callback::BeginOptimization(*this, function, iterate,
callbacks...);
for (size_t i = 1; i != maxIterations && !terminate; ++i)
{
for (size_t j = 0, f = 0; f < numFunctions; j++)
{
@@ -89,36 +125,43 @@ double IQN::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
const size_t effectiveBatchSize = std::min(batchSize, numFunctions -
it * batchSize);
if (arma::norm(iterateVec - t.slice(it)) > 0)
if (arma::norm(iterate - t[it]) > 0)
{
function.Gradient(iterate, it * batchSize, gradient,
effectiveBatchSize);
gradient /= effectiveBatchSize;
const arma::mat s = iterateVec - t.slice(it);
const arma::mat yy = arma::vectorise(gradient - y.slice(it));
terminate |= Callback::Gradient(*this, function, iterate, gradient,
callbacks...);
const arma::mat stochasticHessian = Q.slice(it) + yy * yy.t() /
arma::as_scalar(yy.t() * s) - Q.slice(it) * s * s.t() *
Q.slice(it) / arma::as_scalar(s.t() * Q.slice(it) * s);
const BaseMatType s = arma::vectorise(iterate - t[it]);
const BaseGradType yy = arma::vectorise(gradient - y[it]);
const BaseGradType stochasticHessian = Q[it] + yy * yy.t() /
arma::as_scalar(yy.t() * s) - Q[it] * s * s.t() *
Q[it] / arma::as_scalar(s.t() * Q[it] * s);
// Update aggregate Hessian approximation.
B += (1.0 / numBatches) * (stochasticHessian - Q.slice(it));
B += (1.0 / numBatches) * (stochasticHessian - Q[it]);
// Update aggregate Hessian-variable product.
u += (1.0 / numBatches) * (stochasticHessian * iterateVec -
Q.slice(it) * t.slice(it));
u += arma::reshape((1.0 / numBatches) * (stochasticHessian *
arma::vectorise(iterate) - Q[it] * arma::vectorise(t[it])),
u.n_rows, u.n_cols);;
// Update aggregate gradient.
g += (1.0 / numBatches) * (gradient - y.slice(it));
g += (1.0 / numBatches) * (gradient - y[it]);
// Update the function information tables.
Q.slice(it) = stochasticHessian;
y.slice(it) = gradient;
t.slice(it) = iterateVec;
Q[it] = std::move(stochasticHessian);
y[it] = std::move(gradient);
t[it] = iterate;
iterateVec = stepSize * B.i() * (u - gVec) + (1 - stepSize) *
iterateVec;
iterate = arma::reshape(stepSize * B.i() * (u.t() - arma::vectorise(g)),
iterate.n_rows, iterate.n_cols) + (1 - stepSize) * iterate;
terminate |= Callback::StepTaken(*this, function, iterate,
callbacks...);
}
f += effectiveBatchSize;
@@ -128,7 +171,12 @@ double IQN::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
for (size_t f = 0; f < numFunctions; f += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - f);
overallObjective += function.Evaluate(iterate, f, effectiveBatchSize);
const ElemType objective = function.Evaluate(iterate, f,
effectiveBatchSize);
overallObjective += objective;
Callback::Evaluate(*this, function, iterate, objective,
callbacks...);
}
overallObjective /= numFunctions;
@@ -140,6 +188,8 @@ double IQN::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
{
Warn << "IQN: converged to " << overallObjective << "; terminating"
<< " with failure. Try a smaller step size?" << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
@@ -147,6 +197,8 @@ double IQN::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
{
Info << "IQN: minimized within tolerance " << tolerance << "; "
<< "terminating optimization." << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
}
@@ -154,6 +206,7 @@ double IQN::Optimize(DecomposableFunctionType& function, arma::mat& iterate)
Info << "IQN: maximum iterations (" << maxIterations << ") reached; "
<< "terminating optimization." << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
+39 -4
View File
@@ -63,6 +63,8 @@ class KatyushaType
* @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 exactObjective Calculate the exact objective (Default: estimate the
* final objective obtained on the last pass over the data).
*/
KatyushaType(const double convexity = 1.0,
const double lipschitz = 10.0,
@@ -70,20 +72,45 @@ class KatyushaType
const size_t maxIterations = 1000,
const size_t innerIterations = 0,
const double tolerance = 1e-5,
const bool shuffle = true);
const bool shuffle = true,
const bool exactObjective = false);
/**
* Optimize the given function using Katyusha. The given starting point will
* be modified to store the finishing point of the algorithm, and the final
* objective value is returned.
*
* @tparam DecomposableFunctionType Type of the function to be optimized.
* @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 DecomposableFunctionType>
double Optimize(DecomposableFunctionType& function, arma::mat& iterate);
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(SeparableFunctionType& function,
MatType& iterate,
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 convexity parameter.
double Convexity() const { return convexity; }
@@ -120,6 +147,11 @@ class KatyushaType
//! Modify whether or not the individual functions are shuffled.
bool& Shuffle() { return shuffle; }
//! Get whether or not the actual objective is calculated.
bool ExactObjective() const { return exactObjective; }
//! Modify whether or not the actual objective is calculated.
bool& ExactObjective() { return exactObjective; }
private:
//! The convexity regularization term.
double convexity;
@@ -142,6 +174,9 @@ class KatyushaType
//! Controls whether or not the individual functions are shuffled when
//! iterating.
bool shuffle;
//! Controls whether or not the actual Objective value is calculated.
bool exactObjective;
};
// Convenience typedefs.
@@ -27,24 +27,46 @@ KatyushaType<Proximal>::KatyushaType(
const size_t maxIterations,
const size_t innerIterations,
const double tolerance,
const bool shuffle) :
const bool shuffle,
const bool exactObjective) :
convexity(convexity),
lipschitz(lipschitz),
batchSize(batchSize),
maxIterations(maxIterations),
innerIterations(innerIterations),
tolerance(tolerance),
shuffle(shuffle)
shuffle(shuffle),
exactObjective(exactObjective)
{ /* Nothing to do. */ }
//! Optimize the function (minimize).
template<bool Proximal>
template<typename DecomposableFunctionType>
double KatyushaType<Proximal>::Optimize(
DecomposableFunctionType& function,
arma::mat& iterate)
template<typename SeparableFunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
KatyushaType<Proximal>::Optimize(
SeparableFunctionType& function,
MatType& iterateIn,
CallbackTypes&&... callbacks)
{
traits::CheckDecomposableFunctionTypeAPI<DecomposableFunctionType>();
// Convenience typedefs.
typedef typename MatType::elem_type ElemType;
typedef typename MatTypeTraits<MatType>::BaseMatType BaseMatType;
typedef typename MatTypeTraits<GradType>::BaseMatType BaseGradType;
traits::CheckSeparableFunctionTypeAPI<SeparableFunctionType,
BaseMatType, BaseGradType>();
RequireFloatingPointType<BaseMatType>();
RequireFloatingPointType<BaseGradType>();
RequireSameInternalTypes<BaseMatType, BaseGradType>();
BaseMatType& iterate = (BaseMatType&) iterateIn;
// Controls early termination of the optimization process.
bool terminate = false;
// Find the number of functions to use.
const size_t numFunctions = function.NumFunctions();
@@ -74,29 +96,36 @@ double KatyushaType<Proximal>::Optimize(
normalizer = 1.0 / normalizer;
// To keep track of where we are and how things are going.
double overallObjective = 0;
double lastObjective = DBL_MAX;
ElemType overallObjective = 0;
ElemType lastObjective = DBL_MAX;
// Now iterate!
arma::mat gradient(iterate.n_rows, iterate.n_cols);
arma::mat fullGradient(iterate.n_rows, iterate.n_cols);
arma::mat gradient0(iterate.n_rows, iterate.n_cols);
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
BaseGradType fullGradient(iterate.n_rows, iterate.n_cols);
BaseGradType gradient0(iterate.n_rows, iterate.n_cols);
arma::mat iterate0 = iterate;
arma::mat y = iterate;
arma::mat z = iterate;
arma::mat w = arma::zeros<arma::mat>(iterate.n_rows, iterate.n_cols);
BaseMatType iterate0 = iterate;
BaseMatType y = iterate;
BaseMatType z = iterate;
BaseMatType w(iterate.n_rows, iterate.n_cols);
w.zeros();
const size_t actualMaxIterations = (maxIterations == 0) ?
std::numeric_limits<size_t>::max() : maxIterations;
for (size_t i = 0; i < actualMaxIterations; ++i)
terminate |= Callback::BeginOptimization(*this, function, iterate,
callbacks...);
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
{
// Calculate the objective function.
overallObjective = 0;
for (size_t f = 0; f < numFunctions; f += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - f);
overallObjective += function.Evaluate(iterate0, f, effectiveBatchSize);
const ElemType objective = function.Evaluate(iterate0, f,
effectiveBatchSize);
overallObjective += objective;
Callback::Evaluate(*this, function, iterate0, objective, callbacks...);
}
if (std::isnan(overallObjective) || std::isinf(overallObjective))
@@ -104,6 +133,8 @@ double KatyushaType<Proximal>::Optimize(
Warn << "Katyusha: converged to " << overallObjective
<< "; terminating with failure. Try a smaller step size?"
<< std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
@@ -111,6 +142,8 @@ double KatyushaType<Proximal>::Optimize(
{
Info << "Katyusha: minimized within tolerance " << tolerance
<< "; terminating optimization." << std::endl;
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
@@ -119,6 +152,8 @@ double KatyushaType<Proximal>::Optimize(
// Compute the full gradient.
size_t effectiveBatchSize = std::min(batchSize, numFunctions);
function.Gradient(iterate, 0, fullGradient, effectiveBatchSize);
terminate |= Callback::Gradient(*this, function, iterate, fullGradient,
callbacks...);
for (size_t f = effectiveBatchSize; f < numFunctions;
/* incrementing done manually */)
{
@@ -128,6 +163,9 @@ double KatyushaType<Proximal>::Optimize(
function.Gradient(iterate0, f, gradient, effectiveBatchSize);
fullGradient += gradient;
terminate |= Callback::Gradient(*this, function, iterate0, gradient,
callbacks...);
f += effectiveBatchSize;
}
fullGradient /= (double) numFunctions;
@@ -153,15 +191,23 @@ double KatyushaType<Proximal>::Optimize(
effectiveBatchSize = std::min(batchSize, numFunctions - currentFunction);
iterate = tau1 * z + tau2 * iterate0 + (1 - tau1 - tau2) * y;
terminate |= Callback::StepTaken(*this, function, iterate,
callbacks...);
// Calculate variance reduced gradient.
function.Gradient(iterate, currentFunction, gradient,
effectiveBatchSize);
terminate |= Callback::Gradient(*this, function, iterate, gradient,
callbacks...);
function.Gradient(iterate0, currentFunction, gradient0,
effectiveBatchSize);
terminate |= Callback::Gradient(*this, function, iterate0, gradient0,
callbacks...);
// By the minimality definition of z_{k + 1}, we have that:
// z_{k+1} z_k + \alpha * \sigma_{k+1} + \alpha g = 0.
arma::mat zNew = z - alpha * (fullGradient + (gradient - gradient0) /
BaseMatType zNew = z - alpha * (fullGradient + (gradient - gradient0) /
(double) batchSize);
// Proximal update, choose between Option I and Option II. Shift relative
@@ -197,13 +243,22 @@ double KatyushaType<Proximal>::Optimize(
Info << "Katyusha: maximum iterations (" << maxIterations << ") reached"
<< "; terminating optimization." << std::endl;
// Calculate final objective.
overallObjective = 0;
for (size_t i = 0; i < numFunctions; i += batchSize)
// Calculate final objective if exactObjective is set to true.
if (exactObjective)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - i);
overallObjective += function.Evaluate(iterate, i, effectiveBatchSize);
overallObjective = 0;
for (size_t i = 0; i < numFunctions; i += batchSize)
{
const size_t effectiveBatchSize = std::min(batchSize, numFunctions - i);
const ElemType objective = function.Evaluate(iterate, i,
effectiveBatchSize);
overallObjective += objective;
Callback::Evaluate(*this, function, iterate, objective, callbacks...);
}
}
Callback::EndOptimization(*this, function, iterate, callbacks...);
return overallObjective;
}
+58 -30
View File
@@ -59,14 +59,6 @@ class L_BFGS
const double minStep = 1e-20,
const double maxStep = 1e20);
/**
* Return the point where the lowest function value has been found.
*
* @return arma::vec representing the point and a double with the function
* value at that point.
*/
const std::pair<arma::mat, double>& MinPointIterate() const;
/**
* Use L-BFGS to optimize the given function, starting at the given iterate
* point and finding the minimum. The maximum number of iterations is set in
@@ -76,12 +68,36 @@ class L_BFGS
* algorithm, and the final objective value is returned.
*
* @tparam FunctionType 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; must have Evaluate() and Gradient().
* @param iterate Starting point (will be modified).
* @param callbacks Callback functions.
* @return Objective value of the final point.
*/
template<typename FunctionType>
double Optimize(FunctionType& function, arma::mat& iterate);
template<typename FunctionType,
typename MatType,
typename GradType,
typename... CallbackTypes>
typename std::enable_if<IsArmaType<GradType>::value,
typename MatType::elem_type>::type
Optimize(FunctionType& function,
MatType& iterate,
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 memory size.
size_t NumBasis() const { return numBasis; }
@@ -147,6 +163,8 @@ class L_BFGS
double minStep;
//! Maximum step of the line search.
double maxStep;
//! Controls early termination of the optimization process.
bool terminate;
/**
* Calculate the scaling factor, gamma, which is used to scale the Hessian
@@ -158,10 +176,11 @@ class L_BFGS
* @param s Differences between the iterate and old iterate matrix.
* @param y Differences between the gradient and the old gradient matrix.
*/
template<typename MatType, typename CubeType>
double ChooseScalingFactor(const size_t iterationNum,
const arma::mat& gradient,
const arma::cube& s,
const arma::cube& y);
const MatType& gradient,
const CubeType& s,
const CubeType& y);
/**
* Perform a back-tracking line search along the search direction to
@@ -173,17 +192,24 @@ class L_BFGS
* @param iterate The initial point to begin the line search from.
* @param gradient The gradient at the initial point.
* @param searchDirection A vector specifying the search direction.
* @param stepSize Variable the calculated step size will be stored in.
* @param finalStepSize The resulting step size (0 if no step).
* @param callbacks Callback functions.
*
* @return false if no step size is suitable, true otherwise.
*/
template<typename FunctionType>
template<typename FunctionType,
typename ElemType,
typename MatType,
typename GradType,
typename... CallbackTypes>
bool LineSearch(FunctionType& function,
double& functionValue,
arma::mat& iterate,
arma::mat& gradient,
arma::mat& newIterateTmp,
const arma::mat& searchDirection);
ElemType& functionValue,
MatType& iterate,
GradType& gradient,
MatType& newIterateTmp,
const GradType& searchDirection,
double& finalStepSize,
CallbackTypes&... callbacks);
/**
* Find the L-BFGS search direction.
@@ -195,12 +221,13 @@ class L_BFGS
* @param y Differences between the gradient and the old gradient matrix.
* @param searchDirection Vector to store search direction in.
*/
void SearchDirection(const arma::mat& gradient,
template<typename MatType, typename CubeType>
void SearchDirection(const MatType& gradient,
const size_t iterationNum,
const double scalingFactor,
const arma::cube& s,
const arma::cube& y,
arma::mat& searchDirection);
const CubeType& s,
const CubeType& y,
MatType& searchDirection);
/**
* Update the y and s matrices, which store the differences
@@ -215,13 +242,14 @@ class L_BFGS
* @param s Differences between the iterate and old iterate matrix.
* @param y Differences between the gradient and the old gradient matrix.
*/
template<typename MatType, typename GradType, typename CubeType>
void UpdateBasisSet(const size_t iterationNum,
const arma::mat& iterate,
const arma::mat& oldIterate,
const arma::mat& gradient,
const arma::mat& oldGradient,
arma::cube& s,
arma::cube& y);
const MatType& iterate,
const MatType& oldIterate,
const GradType& gradient,
const GradType& oldGradient,
CubeType& s,
CubeType& y);
};
} // namespace ens

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