diff --git a/404.md b/404.md new file mode 100644 index 000000000..0137fd723 --- /dev/null +++ b/404.md @@ -0,0 +1,7 @@ +--- +layout: default +title: 404 - Page not found +--- +404 - Page not found +==================== +Sorry, we couldn’t find the requested URL. You can try again by going [back to the homepage]({{ site.baseurl }}). diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..9d12efd0d --- /dev/null +++ b/Gemfile @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +# gem "rails" + +gem "jekyll", "~> 3.8" +gem "jekyll-paginate" +gem "jekyll-sitemap" +gem "jekyll-redirect-from" + + +gem "jekyll-watch", "~> 2.1" + +gem "ruby_dep", "~> 1.5" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..db87f54ab --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,73 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + colorator (1.1.0) + concurrent-ruby (1.1.4) + em-websocket (0.5.1) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + eventmachine (1.2.7) + ffi (1.10.0) + forwardable-extended (2.6.0) + http_parser.rb (0.6.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jekyll (3.8.5) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 0.7) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (~> 1.14) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-paginate (1.1.0) + jekyll-redirect-from (0.14.0) + jekyll (~> 3.3) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-sitemap (1.2.0) + jekyll (~> 3.3) + jekyll-watch (2.1.2) + listen (~> 3.0) + kramdown (1.17.0) + liquid (4.0.1) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + mercenary (0.3.6) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (3.0.3) + rb-fsevent (0.10.3) + rb-inotify (0.10.0) + ffi (~> 1.0) + rouge (3.3.0) + ruby_dep (1.5.0) + safe_yaml (1.0.4) + sass (3.7.3) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + +PLATFORMS + ruby + +DEPENDENCIES + jekyll (~> 3.8) + jekyll-paginate + jekyll-redirect-from + jekyll-sitemap + jekyll-watch (~> 2.1) + ruby_dep (~> 1.5) + +BUNDLED WITH + 1.17.1 diff --git a/_includes/anchor_headings.html b/_includes/anchor_headings.html new file mode 100644 index 000000000..25397df93 --- /dev/null +++ b/_includes/anchor_headings.html @@ -0,0 +1,100 @@ +{% capture headingsWorkspace %} + {% comment %} + Version 1.0.3 + https://github.com/allejo/jekyll-anchor-headings + + "Be the pull request you wish to see in the world." ~Ben Balter + + Usage: + {% include anchor_headings.html html=content %} + + Parameters: + * html (string) - the HTML of compiled markdown generated by kramdown in Jekyll + + Optional Parameters: + * beforeHeading (bool) : false - Set to true if the anchor should be placed _before_ the heading's content + * anchorBody (string) : '' - The content that will be placed inside the anchor; the `%heading%` placeholder is available + * anchorClass (string) : '' - The class(es) that will be used for each anchor. Separate multiple classes with a space + * anchorTitle (string) : '' - The `title` attribute that will be used for anchors + * h_min (int) : 1 - The minimum header level to build an anchor for; any header lower than this value will be ignored + * h_max (int) : 6 - The maximum header level to build an anchor for; any header greater than this value will be ignored + * bodyPrefix (string) : '' - Anything that should be inserted inside of the heading tag _before_ its anchor and content + * bodySuffix (string) : '' - Anything that should be inserted inside of the heading tag _after_ its anchor and content + + Output: + The original HTML with the addition of anchors inside of all of the h1-h6 headings. + {% endcomment %} + + {% assign minHeader = include.h_min | default: 1 %} + {% assign maxHeader = include.h_max | default: 6 %} + {% assign beforeHeading = include.beforeHeading %} + {% assign nodes = include.html | split: ' + {% if headerLevel == 0 %} + {% if nextChar != '<' and nextChar != '' %} + {% capture node %}' | first }}>{% endcapture %} + {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %} + + + {% capture anchor %}{% endcapture %} + + {% if html_id and headerLevel >= minHeader and headerLevel <= maxHeader %} + {% capture anchor %}href="#{{ html_id}}"{% endcapture %} + + {% if include.anchorClass %} + {% capture anchor %}{{ anchor }} class="{{ include.anchorClass }}"{% endcapture %} + {% endif %} + + {% if include.anchorTitle %} + {% capture anchor %}{{ anchor }} title="{{ include.anchorTitle | replace: '%heading%', header }}"{% endcapture %} + {% endif %} + + {% capture anchor %}{{ include.anchorBody | replace: '%heading%', header | default: '' }}{% endcapture %} + + + {% if beforeHeading %} + {% capture anchor %}{{ anchor }} {% endcapture %} + {% else %} + {% capture anchor %} {{ anchor }}{% endcapture %} + {% endif %} + {% endif %} + + {% capture new_heading %} + diff --git a/_includes/head-doc.html b/_includes/head-doc.html new file mode 100644 index 000000000..dac6d06ea --- /dev/null +++ b/_includes/head-doc.html @@ -0,0 +1,20 @@ + + + + + + + {%if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} + + + + + + + + + + + + + diff --git a/_includes/head.html b/_includes/head.html new file mode 100644 index 000000000..31a2e6e34 --- /dev/null +++ b/_includes/head.html @@ -0,0 +1,19 @@ + + + + + + + {%if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} + + + + + + + + + + + + diff --git a/_includes/header-custom.html b/_includes/header-custom.html new file mode 100644 index 000000000..e684da216 --- /dev/null +++ b/_includes/header-custom.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/_includes/header.html b/_includes/header.html new file mode 100644 index 000000000..1e16aad53 --- /dev/null +++ b/_includes/header.html @@ -0,0 +1,46 @@ +
+
+
ensmallen
+
+
mlpack
fast, +flexible C++ machine learning library
+
+
+ diff --git a/_includes/mathjax.html b/_includes/mathjax.html new file mode 100644 index 000000000..4a7a4f9d8 --- /dev/null +++ b/_includes/mathjax.html @@ -0,0 +1,22 @@ +{% if page.mathjax %} + + + +{% endif %} diff --git a/_includes/menu.html b/_includes/menu.html new file mode 100644 index 000000000..acfaa431c --- /dev/null +++ b/_includes/menu.html @@ -0,0 +1,30 @@ + diff --git a/_includes/nav-footer-custom.html b/_includes/nav-footer-custom.html new file mode 100644 index 000000000..a9239e799 --- /dev/null +++ b/_includes/nav-footer-custom.html @@ -0,0 +1,5 @@ +
+ + + +
\ No newline at end of file diff --git a/_includes/nav-footer.html b/_includes/nav-footer.html new file mode 100644 index 000000000..0c26f5f67 --- /dev/null +++ b/_includes/nav-footer.html @@ -0,0 +1,5 @@ +
+ + version {{ site.version }} + +
\ No newline at end of file diff --git a/_includes/script.html b/_includes/script.html new file mode 100644 index 000000000..b10f08b04 --- /dev/null +++ b/_includes/script.html @@ -0,0 +1 @@ + diff --git a/_layouts/default-nav.html b/_layouts/default-nav.html new file mode 100644 index 000000000..c9a891770 --- /dev/null +++ b/_layouts/default-nav.html @@ -0,0 +1,73 @@ + + + +{% include head.html %} + + +{% include mathjax.html %} +
+ + + {% include menu.html %} + + + + + + + + {% if site.custom_header == true %} + {% include header-custom.html %} + {% else %} + {% include header.html %} + {% endif %} + + +
+ +
+ + {{ content }} + +
+ + + {% if paginator.total_pages > 1 %} + + {% endif %} + +
+ + + {% include footer.html %} + + + {% include script.html %} + +
+ + diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 000000000..c9a891770 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,73 @@ + + + +{% include head.html %} + + +{% include mathjax.html %} +
+ + + {% include menu.html %} + + + + + + + + {% if site.custom_header == true %} + {% include header-custom.html %} + {% else %} + {% include header.html %} + {% endif %} + + +
+ +
+ + {{ content }} + +
+ + + {% if paginator.total_pages > 1 %} + + {% endif %} + +
+ + + {% include footer.html %} + + + {% include script.html %} + +
+ + diff --git a/_layouts/docs-nav.html b/_layouts/docs-nav.html new file mode 100644 index 000000000..ca907a458 --- /dev/null +++ b/_layouts/docs-nav.html @@ -0,0 +1,73 @@ + + + +{% include head-doc.html %} + + +{% include mathjax.html %} +
+ + + {% include menu.html %} + + + + + + + + {% if site.custom_header == true %} + {% include header-custom.html %} + {% else %} + {% include header.html %} + {% endif %} + + +
+ +
+ + {% include anchor_headings.html html=content anchorBody="" h_max=3 %} + +
+ + + {% if paginator.total_pages > 1 %} + + {% endif %} + +
+ + + {% include footer.html %} + + + {% include script.html %} + +
+ + diff --git a/_layouts/docs.html b/_layouts/docs.html new file mode 100644 index 000000000..ca907a458 --- /dev/null +++ b/_layouts/docs.html @@ -0,0 +1,73 @@ + + + +{% include head-doc.html %} + + +{% include mathjax.html %} +
+ + + {% include menu.html %} + + + + + + + + {% if site.custom_header == true %} + {% include header-custom.html %} + {% else %} + {% include header.html %} + {% endif %} + + +
+ +
+ + {% include anchor_headings.html html=content anchorBody="" h_max=3 %} + +
+ + + {% if paginator.total_pages > 1 %} + + {% endif %} + +
+ + + {% include footer.html %} + + + {% include script.html %} + +
+ + diff --git a/_layouts/page.html b/_layouts/page.html new file mode 100644 index 000000000..7ac330acc --- /dev/null +++ b/_layouts/page.html @@ -0,0 +1,8 @@ +--- +layout: default +--- +
+ + {{ content }} + +
diff --git a/_layouts/post.html b/_layouts/post.html new file mode 100644 index 000000000..32158a60f --- /dev/null +++ b/_layouts/post.html @@ -0,0 +1,13 @@ +--- +layout: default +--- +
+

{%if page.header %}{{ page.header }}{% else %}{{ page.title }}{% endif %}

+ +
+ + {{ content }} + +
+
+ diff --git a/_sass/base.scss b/_sass/base.scss new file mode 100644 index 000000000..17a082e18 --- /dev/null +++ b/_sass/base.scss @@ -0,0 +1,84 @@ +/* + * Monochrome is a simple, responsive blog theme built for Jekyll. + */ + +/*- Base reset -*/ + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +html, body, h1, h2, h3, h4, h5, h6, p, ul, ol, li, img { + margin: 0; + padding: 0; + border: 0; +} + +body { + margin-left: 15px; + margin-right: 15px; +} + +/*- Base color -*/ + +$background-color: #000000; +$text-color: #ccc; +$text-light-color: #ddd; +$text-dark-color: #aaa; + +/*- Base settings -*/ + +html { + background-color: $background-color; + font-size: 16px; + @media (min-width: 940px) { + font-size: 18px; + } + line-height: 1.8; + color: $text-color; +} + +body { + max-width: 1000px; + margin: 0 auto; + padding: 0 10px; +} + +/*- Link -*/ +a { + color: $text-color; + text-decoration: none; + font-weight: 700; + &:hover, + &:focus { + color: darken($text-color, 5%); + } +} + +/* Common classes */ + +.f-right { float: right; } +.f-left { float: left; } +.clear { clear:both; } + +// to align div vertically and horizontally in center of another div +.parent { display: flex;} +.inner { align-self: center; } + +// justify +.justify-center { justify-content: center; } +.justify-spaceBetween { justify-content: space-between; } + +.w100 { width: 100% } +.h100 { height: 100% } +.wh100 { width: 100%; height: 100%} + +// position +.absolute { position: absolute; } +.relative { position: relative; } +.top { top: 0 } +.bottom { bottom: 0 } +.right { right: 0 } +.left { left: 0 } diff --git a/_sass/custom.scss b/_sass/custom.scss new file mode 100644 index 000000000..db4a4f50a --- /dev/null +++ b/_sass/custom.scss @@ -0,0 +1,256 @@ +/*- Custom style -*/ + +.link-special { + padding:5px; + border:1px solid black; +} + +body { + max-width: 1600px; +} + +ul, ol { + margin-left: 30px; +} + +ul ul { + margin-bottom: 0px; +} + +ul ul li a { + font-weight: normal; +} + +ul ul ul li a { + font-style: italic; +} + +h1 { + border-top: 5px solid #000000; +} + +h2 { +/* border-top: 2px solid #000000;*/ +} + +/*h3 { + float: left; + margin-right: 1em; +}*/ + +h6 { + font-style: italic; + font-weight: normal; +} + +div.highlight { + margin-left: 30px; + margin-right: 30px; + font-size: 85%; + background: #202020; + padding-left: 10px; + padding-right: 10px; + margin-bottom: 25px; + display: inline-block; + min-width: calc(100% - 60px); +} + +code { + color: #dddddd; + background: none; +} + +code.highlighter-rouge { + font-size: 85%; + line-height: 85%; +} + +li { + line-height: 150%; +} + +p { + line-height: 135%; +} + +table { + padding-left: 30px; + padding-bottom: 30px; + max-width: calc(100% - 30px); +} + +table th { + padding-left: 15px; + padding-right: 15px; + overflow-wrap: break-word; + background: #404040; +} + +table td { + padding-left: 10px; + padding-right: 10px; + overflow-wrap: break-word; +} + +table tr:nth-child(even), table tr:nth-child(even) td, table tr:nth-child(even) th { + background: #101010; +} + +table tr:nth-child(odd), table tr:nth-child(odd) td, table tr:nth-child(even) th { + background: #202020; +} + +a +{ + color: #bb0000; +} + +a:hover +{ + color: #eab72c; +} + +h1#ensmallen-documentation { + text-align: center; + border-top: none; + padding-top: 0; +} + +div.MathJax_Display { + color: #000000; +} + +#ens_header { + display: block; + padding: 0; + margin: 0; + width: 100%; +} + +@media (max-width: 940px) { + #ens_header { + margin-bottom: 20px; + border-bottom: 5px solid #eab72c; + } +} + +#ens_header_row + { + display: flex; + padding: 0; + margin: 0; + align-items: center; + } + +#ens_header_cell_logo_img + { +/* border: 1px solid #999999; */ + flex: 9; + vertical-align: bottom; + text-align: right; + padding-top: 1.0em; + min-width: 130px; + } + +#ens_header_spacer { + flex: 1; +} + +#ens_logo_img + { +/* border: 1px solid #999999; */ + max-width: 100%; + max-height: 100%; + } + +#ens_header_cell_logo_txt + { +/* border: 1px solid #999999; */ + flex: 90; + text-align: left; + line-height: 150%; + } + +#nav-links { + background: #222222; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 50px; + margin-bottom: 2em; + border-top: 5px solid #111111; + border-bottom: 5px solid #111111; +} + +#nav-links a { + color: #eab72c; + font-weight: bold; + padding-left: 5px; + padding-right: 5px; + padding-bottom: 3px; + padding-top: 5px; + margin-left: 5px; + margin-right: 5px; + font-size: 110%; +} + +#nav-links a:hover { + color: #bb0000; + font-weight: bold; +} + +#nav-links a.active:hover { + color: #eab72c; + font-weight: bold; +} + +#nav-links a.active { + border-bottom: 5px solid #eab72c; +} + +@media (min-width: 940px) { +#nav.menu-open { + display: none; +} +} + +body +{ + background: #000000; +} + +strong +{ + color: #ddd; +} + +h2 +{ + color: #ddd; + font-size: 175%; + border-bottom: 1px solid #bb0000; + margin-bottom: 10px; + padding-bottom: 1px; + margin-top: 2em; +} + +h4 +{ + font-size: 130%; + line-height: 1.0em; + padding-top: 0; + padding-left: 20px; + padding-bottom: 1em; + color: #eab72c; +} + +h3 +{ + font-size: 130%; + line-height: 1.0em; + margin-left: 20px; + color: #ddd; + display: inline-block; + border-bottom: 1px solid #eab72c; + padding-bottom: 1px; + margin-bottom: 1em; +} diff --git a/_sass/docs.scss b/_sass/docs.scss new file mode 100644 index 000000000..d52f9f7c9 --- /dev/null +++ b/_sass/docs.scss @@ -0,0 +1,205 @@ +body +{ + padding-left: 220px; +} + +/* Needs to be updated as we add more languages... */ +h2#mlpack-overview, +h2#mlpack-overview-1, +h2#mlpack-overview-2, +h2#mlpack-overview-3, +h2#mlpack-overview-4, +h2#mlpack-overview-5 +{ + display: none; +} + +/* Don't display other languages by default. */ +div.language-title, div.language-header, div.language-decl, +div.language-detail-link, div.language-types, div.language-section +{ + display: none; +} + +/* Just display cli documentation. */ +div.language-title#cli, div.language-header#cli, div.language-decl#cli, +div.language-detail-link#cli, div.language-types#cli, div.language-section#cli +{ + display: none; +} + +@media (max-width: 940px) { + div#header { + display: none; + } + + body { + padding-left: 10px !important; + } +} + +div#header +{ + padding-top: 10px; + width: 210px; + background: #000000; + border-right: 2px solid #333333; + height: 100%; + position: fixed; + z-index: 1; + top: 0; + left: 0; + overflow-y: scroll; + overflow-x: hidden; + padding-left: 5px; +} + +div#header .language-select-div select, +div#header .version-select-div select +{ + color: #ddd; + background: transparent; + border: none; + height: 29px; + padding: 5px; + width: 190px; +} + +div#header select option +{ + color: #ddd; + background: #000; + border: 2px solid #333333; +} + +div#header .language-select-div +{ + color: #ddd; + border: 2px solid #333333; + background: url('../res/menu_bg.png') no-repeat 96% 0; + height: 34px; + width: 180px; + overflow: hidden; + margin-bottom: 20px; + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +div#header .version-select-div +{ + color: #ddd; + border: 2px solid #333333; + background: url('../res/menu_bg.png') no-repeat 96% 0; + height: 34px; + width: 180px; + overflow: hidden; + margin-bottom: 2px; + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +div#header ul +{ + padding-top: 5px; + margin-bottom: 5px; + color: #bb2000; + display: table; + text-align: left; + padding-left: 0px; + margin-left: 15px; + font-size: 75%; +} + +div#header ul li +{ + line-height: 1.3em; +} + +div#header ul li a +{ + color: #eab72c; + font-weight: none; +} + +div#header ul li a:hover +{ + color: #ddd; + font-weight: none; +} + +span.special +{ + font-size: 85%; + color: #eab72c; +} + +div.category +{ + text-align: left; +} + +div.category h5 +{ + text-align: left; + margin-top: 0; + margin-bottom: 0; + color: #ddd; + font-size: 100%; + font-weight: normal; + padding: 0; +} + +div.language-types li code +{ + font-weight: bold; + color: #eab72c; +} + +div.language-types li +{ + padding-bottom: 1em; +} + +div.language-section table td a code:hover +{ + color: #bb2000; +} + +table { + padding-left: 30px; + padding-bottom: 30px; + max-width: calc(100% - 30px); +} + +table th { + padding-left: 15px; + padding-right: 15px; + overflow-wrap: break-word; + background: #404040; +} + +table td { + padding-left: 10px; + padding-right: 10px; + overflow-wrap: break-word; +} + +table tr:nth-child(even), table tr:nth-child(even) td, table tr:nth-child(even) th { + background: #101010; +} + +table tr:nth-child(odd), table tr:nth-child(odd) td, table tr:nth-child(even) th { + background: #202020; +} + +img.link_icon +{ + height: 0.5em; + display: inline; + margin: 0; + padding: 0; +} diff --git a/_sass/layout.scss b/_sass/layout.scss new file mode 100644 index 000000000..00f540e07 --- /dev/null +++ b/_sass/layout.scss @@ -0,0 +1,265 @@ +/* -- General Layout -- */ + +/* Navigation */ + +#nav, #nav-left { + a { + display: block; + color: $text-color; + padding: 0.33334em 0; + font-size: 1.5em; + font-weight: 400; + @media (min-width: 940px) { + font-size: 1em; + } + &:hover { + background-color: lighten($text-light-color, 5%); + } + } + span { + font-weight: 200; + } +} + +#nav { + @include nav-position(right); +} + +#nav-left { + @include nav-position(left); +} + +/* Toggle class to open menu */ + +#nav.menu-open { + @include open(-14rem); +} + +#nav-left.menu-open-left { + @include open(14rem); +} + +#nav-links { + @media (max-width: 940px) { + display:none; + } + a { + padding-left: 10px; + color: $text-color; + font-weight: 300 + } +} + +/* Separator after menu */ + +#nav-list:after { + display: block; + content: ''; + width: 5rem; + height: 1px; + margin: 23px auto; + background-color: $text-color; +} + +/* Icon menu */ + +#nav-menu { + @include icon-position(right); + @media (min-width: 940px) { + display:none; + } +} + +#nav-menu-left { + @include icon-position(left); + @media (min-width: 940px) { + display:none; + } +} + +#menu { + height: 4px; + width: 1.5em; + background-color: $text-color; + margin-top: 8px; + &:after, &:before { + content: ""; + display: block; + position: relative; + height: 4px; + width: 1.5em; + background-color: $text-color; + transition: all 0.3s ease-in; + } + &:before { + top: -8px; + } + &:after { + top: 4px; + } + &.btn-close { + background: none; + } + &.btn-close:before { + top: 0; + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); + background-color: $text-color; + } + &.btn-close:after { + top: -4px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + background-color: $text-color; + } +} + +/* Main content */ + +.fixed { + position: fixed; + width: 100%; + @media (min-width: 940px) { + position: static; + } +} + +#container { + margin:0 auto; +} + +#header { + border-bottom: 1px solid rgba(0,0,0,0.14); + text-align: center; + margin-bottom: 3em; + height: 5em; + position: relative; + a { + text-decoration: none; + display: inline-block; + } + div { + margin: 0 auto; + } + h1 { + font-size: 2em; + padding-bottom: 0; + span { + color: $text-light-color; + font-weight: 300; + } + } +} + +/* Posts */ + +#posts { + li { + list-style-type: none; + } +} + +#post-page { + margin-bottom: 1.5em; + @media (min-width: 940px) { + margin-bottom: 1.3334em; + } +} + +.post+.post:before { + display: block; + content: ''; + width: 5rem; + height: 1px; + margin: 23px auto; + background-color: lighten($text-color, 70%); + } + +.by-line { + display: block; + color: lighten($text-color, 25%); + line-height: 1.8em; + margin-bottom: 1.5em; /* 24px/16px */ + font-weight: 200; + @media (min-width: 940px) { + display: block; + color: lighten($text-color, 25%); + line-height: 1.8em; + margin-bottom: 1.3334em; /* 24px/18px */ + font-weight: 200; + } +} + +img { + max-width: 100%; + display: block; + margin: 0 auto; + margin-bottom: 24px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + border-radius: 4px; +} + +img[title="Monochrome"] { + box-shadow: 0 2px 6px #ddd; + } + +code { + color: lighten($text-color, 35%); + background-color: lighten($background-color, 35%); +} + +/* Set the vertical rhythm (and padding-left) for lists inside post content */ + +.content ul, .content ol { + line-height: 1.8em; + padding-left: 1.5em; + @media (min-width: 940px) { + line-height: 1.8em; + } +} + +/* Pages */ + +#page ul, #page ol { + padding-left: 1.5em; +} + +/* Paginator */ + +.pagination { + text-align: center; + margin: 2.666668em; + span { + background-color: darken($background-color, 5%); + color: $text-color; + } + a:hover { + background-color: lighten($text-color, 5%); + } +} +.page-item { + background-color: lighten($text-color, 10%); + color: $background-color; + padding: 4px 8px; + font-weight: 400; + padding: 0.5em 1em; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + border-radius: 4px; +} + +/* Footer */ + +footer { + background-color: $background-color; + color: $text-color; + text-align: center; + padding: 0.6667em 0; +} diff --git a/_sass/mixin.scss b/_sass/mixin.scss new file mode 100644 index 000000000..773431bf7 --- /dev/null +++ b/_sass/mixin.scss @@ -0,0 +1,41 @@ +// -- Mixins -- // + +// Nav menu + +@mixin icon-position($position) { + display: block; + position: fixed; + top: 35px; + #{$position}: 25px; + z-index: 10; + height: 24px; +} + +@mixin open($x) { + -webkit-transform: translateX($x); + -moz-transform: translateX($x); + -ms-transform: translateX($x); + transform: translateX($x); + width: 100%; + @media (min-width: 940px) { + width: 30%; + } +} + +@mixin nav-position($position) { + width: 14rem; + position: fixed; + background-color: $background-color; + top: 0; + bottom: 0; + #{$position}: -14rem; + color: $background-color; + opacity: 0.95; + -webkit-transition: all 0.3s ease-in; + -moz-transition: all 0.3s ease-in; + -ms-transition: all 0.3s ease-in; + transition: all 0.3s ease-in; + z-index: 1; + padding: 72px 0; + text-align: center; +} diff --git a/_sass/syntax.scss b/_sass/syntax.scss new file mode 100644 index 000000000..af9345ed2 --- /dev/null +++ b/_sass/syntax.scss @@ -0,0 +1,66 @@ +/* + * A Github stylesheet to highlight code snippet + * https://github.com/mojombo/tpw/blob/master/css/syntax.css + */ + +// .highlight { background-color: #FFF; } +.lineno { color: darken($background-color, 25%); margin-right: 1em; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #aaaaaa } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #a0a000 } /* Name.Tag */ +.highlight .nv { color: #00a0a0 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #f44 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #44bb44 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ diff --git a/_sass/typography.scss b/_sass/typography.scss new file mode 100644 index 000000000..beec4a2f0 --- /dev/null +++ b/_sass/typography.scss @@ -0,0 +1,103 @@ +/*- Typography -*/ +// +// Based on the typographic scale: 12, 14, 16, 18, 21, 24, 36, 48, 60, 72. +// + +body +{ + font-family : "Roboto", "Helvetica Neue", "Helvetica", "Arial", sans-serif; + font-style: normal; + font-weight: 400; + font-size : 16px; + color: #aaa; +} + +/*- Typography for medium and small screen, based on 16px font-size -*/ + +p, ul, ol { + font-size: 1em; /* 16px */ + line-height: 1.8em; + margin-bottom: 1.5em; /* 24px/16px */ +} + +h1 { + font-size: 2.0em; /* 36px/16px */ + line-height: 1.0em; + color: #fff; + border-bottom: 2px solid #eab72c; + margin-bottom: 0.5em; + margin-top: 0.5em; + width: 100%; +} + +h2 { + font-size: 1.5em; /* 24px/16px */ + line-height: 1.0em; +} + +h3, h4, h5, h6 { + font-size: 1.125em; /* 18px/16px */ + line-height: 1.8em; + padding: 0.66667em 0; /* 12px/18px * 2 (Use padding instead of margin to maintain proximity with paragraph) */ +} + +blockquote { + font-style: italic; + margin: 1.5em; /* 24px/18px */ + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + border-radius: 4px; + background-color: darken($background-color, 5%); + padding: 0 1.5em; /* 24px/18px */ + p, ul, ol { + padding: 1.5em 0; /* 24px/18px */ + } +} + +/*- Typography for big screen, based on 18px font-size -*/ + +@media (min-width: 940px) { //Breakpoint set to 940px + +p, ul, ol { + font-size: 1em; /* 18px */ + line-height: 1.8em; + margin-bottom: 1.3334em; /* 24px/18px */ +} + +h1 { + font-size: 2.0em; /* 36px/16px */ + line-height: 1.0em; + color: #fff; + border-bottom: 2px solid #eab72c; + margin-bottom: 0.5em; + margin-top: 0.5em; + width: 100%; +} + +h2 { + font-size: 2em; /* 36px/18px */ + line-height: 1.0em; +} + +h3, h4, h5, h6 { + font-size: 1.3334em; /* 24px/18px */ + line-height: 1.8em; + padding: 0.5em 0; /* 12px/24px * 2 (Use padding instead of margin to maintain proximity with paragraph) */ +} + +blockquote { + font-style: italic; + margin: 1.3334em; /* 24px/18px */ + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + border-radius: 4px; + background-color: darken($background-color, 5%); + padding: 0 1.33334em; /* 24px/18px */ + p, ul, ol { + padding: 1.33334em 0; /* 24px/18px */ + } +} + +} diff --git a/_src/doxygen/404.html b/_src/doxygen/404.html new file mode 100644 index 000000000..6bef55c7c --- /dev/null +++ b/_src/doxygen/404.html @@ -0,0 +1 @@ + diff --git a/_src/doxygen/doxygen.css b/_src/doxygen/doxygen.css new file mode 100644 index 000000000..224912408 --- /dev/null +++ b/_src/doxygen/doxygen.css @@ -0,0 +1,1838 @@ +/* The standard CSS for doxygen 1.8.13 */ + +p.reference, p.definition { + font-size: 85%; + line-height: 120%; +} + +/* @group Heading Levels */ + +/* +h1.groupheader { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; + color: #ddd; + text-align: center; +} + +h2.groupheader { + color: #ddd; + font-size: 150%; + font-weight: normal; + padding-top: 10px; + padding-bottom: 10px; + width: 100%; +} + +h3.groupheader { + font-size: 100%; +} + +*/ +div.textblock h3 { + font-family: monospace, fixed; + font-weight: normal; + text-align: left !important; + color: #ddd; + border-bottom: none; + line-height: 1.2em; +} + +/* +h3 br { + display: none; +} + +h1, h2, h3, h4, h5, h6 { + -webkit-transition: text-shadow 0.5s linear; + -moz-transition: text-shadow 0.5s linear; + -ms-transition: text-shadow 0.5s linear; + -o-transition: text-shadow 0.5s linear; + transition: text-shadow 0.5s linear; + color: #ddd; + text-align: center; + border-bottom: 15px solid #101010; + padding-top: 15px; + padding-bottom: 15px; + border-top: 15px solid #101010; +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; +}*/ + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd { + margin-top: 2px; +} + +p.starttd { + margin-top: 0px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #202020; + text-align: center; +} + +div.qindex, div.navpath { + width: 610px; + line-height: 140%; + padding-left: 10px; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ddd; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ddd; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited, a.line, a.line:visited { + color: #bb0000; +} + +a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { + color: #bb0000; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; + font-family: monospace, fixed; + font-size: 105%; +} + +div.fragment { + background-color: #202020; + padding-top: 1em; + padding-bottom: 1em; + margin-left: 30px; + margin-right: 30px; + margin-bottom: 25px; + padding-left: 10px; + padding-right: 10px; + min-width: calc(100% - 60px); +} + +div.fragment div.line { + font-family: monospace, fixed; + font-size: 85%; + color: #ddd; + min-height: 11px; + line-height: 1.2em; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + + +div.line { + font-family: monospace, fixed; + font-size: 85%; + color: #ddd; + min-height: 12px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +div.line:after { + content:"\000A"; + white-space: pre; +} + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + + +span.lineno { + padding-right: 4px; + text-align: right; + border-right: 2px solid #0F0; + background-color: #202020; + white-space: pre; +} +span.lineno a:hover { + color: #eab72c; +} + +.lineno { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +div.ah, span.ah { + background-color: black; + font-weight: bold; + color: #ddd; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000 110%); +} + +div.classindex ul { + list-style: none; + padding-left: 0; +} + +div.classindex span.ai { + display: inline-block; +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +div.contents { + margin-top: 10px; + margin-left: 12px; + margin-right: 8px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaInl { + vertical-align: middle; + height: 1.1em; + margin-bottom: 0 !important; + display: inline; +} + +div.center { + text-align: center; + margin: 0px; + margin-top: 10px; + padding: 0px; +} + +div.center img { + border: 0px; + max-width: 600px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #88ff88 +} + +span.keywordtype { + color: #907050 +} + +span.keywordflow { + color: #ffb444 +} + +span.comment { + color: #bb2222 +} + +span.preprocessor { + color: #a08040 +} + +span.stringliteral { + color: #6686f6 +} + +span.charliteral { + color: #88ffff +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #ffffff +} + +span.vhdlkeyword { + color: #a050a0 +} + +span.vhdllogic { + color: #ff2222 +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 15px solid #101010; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; + width: 100%; + background: #202020; +} + +.memberdecls td, .fieldtable tr { + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; + line-height: 120%; + background: #202020 !important; +} + +.memberdecls th { + background: #202020 !important; +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; +} + +.mdescRight { + background-color: #202020; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} +.mdescRight br { + display: none +} +.mdescLeft, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #202020; + border: none; + margin: 4px; + padding: 1px 0 0 8px; + font-family: monospace, fixed; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; +} + +.memSeparator { + border-bottom: 1px solid #101010; + line-height: 1px; + margin: 0px; + padding: 0px; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.mdescRight { + padding-top: 5px; +} + +.memItemRight { + width: 100%; +} + +.memItemRight, .memTemplItemRight { + max-width: 430px; + white-space: nowrap; + color: #ddd; + text-overflow: ellipsis; + overflow: hidden; +} + +.memTemplParams { + color: #ddd; + white-space: nowrap; + font-family: monospace, fixed; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtitle { + border: none; + font-weight: normal; + font-size:120%; + font-family: monospace,fixed; +} + +.permalink +{ + font-size: 65%; + display: inline-block; + vertical-align: middle; +} + +.memtemplate { + color: #ddd; + font-weight: normal; + margin-left: 9px; + line-height: 120%; + font-family: monospace, fixed; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; + -webkit-transition: box-shadow 0.5s linear; + -moz-transition: box-shadow 0.5s linear; + -ms-transition: box-shadow 0.5s linear; + -o-transition: box-shadow 0.5s linear; + transition: box-shadow 0.5s linear; + display: table !important; + width: calc(100% - 5px); + padding-left: 30px; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; +} + +.memname { + font-weight: 400; + margin-left: 6px; + padding: 0px; + font-family: monospace, fixed; + color: #ddd; + line-height: 1.1em; + table-layout: fixed; +/* width: 100%;*/ +} + +.memname td { + vertical-align: bottom; + white-space: nowrap; /* needed to get width in JS */ + padding: 0px; +} + +.memname td.paramtype div.content, td.memItemLeft div.content, +td.memTemplItemLeft div.content, td.memItemRight div.content { + border: 0; + margin: 0; + padding: 0; + vertical-align: bottom; + font-family: monospace, fixed; + line-height: 1.1em; + overflow: hidden; + text-overflow: ellipsis; +} + +td.memItemLeft div.content, td.memTemplItemLeft div.content { + width: 150px; +} + +.memproto, dl.reflist dt { + padding: 6px 0px 6px 0px; + color: #ddd; + font-weight: bold; + background-color: #202020; + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 4px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 4px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 4px; + +} + +.overload { + font-family: "courier new",courier,monospace; + font-size: 65%; +} + +.memdoc, dl.reflist dd { + width: 100%; + padding: 6px 10px 2px 10px; + background-color: #333; + border-top-width: 0; + background-image:url('nav_g.png'); + background-repeat:repeat-x; + /* opera specific markup */ + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + /* firefox specific markup */ + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-bottomright: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #eab72c; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} +.paramname code { + line-height: 14px; +} + +.params, .retval, .exception { + margin-left: 0px; + padding-left: 0px; + line-height: 1.1em; + background: #333 !important; +} + +.tparams { + padding-top: 10px; + padding-bottom: 10px; +} +.tparams tr, .tparams td, .tparams th { + background: #000 !important; +} + +dl table.tparams tr, dl table.tparams td, dl table.tparams th { + background: #333 !important; +} + +.params tr, .params td, .params th, { + background: #333 !important; +} + +.params dt { + margin-bottom: 0.5em; + color: #ddd; +} + +.params .paramname, .retval .paramname, table.params tr td { + font-weight: normal; + vertical-align: top; + background: #333 !important; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + +table.mlabels { + border-spacing: 0px; + padding: 0px; + margin: 0px; +} + +td.mlabels-left { + padding: 0px; + float: left; +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; + clear: both; + display: block; +} + +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #bb2222; + border-top:1px solid #888; + border-left:1px solid #888; + border-right:1px solid #aaa; + border-bottom:1px solid #aaa; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; +} + +span#author { + font-size: 9pt; +} + + + +/* @end */ + +/* these are for tree view inside a (index) page */ + +div.directory { + margin: 10px 0px; + border-top: 1px solid #bb0000; + border-bottom: 1px solid #bb0000; + width: 100%; +} + +.directory table { + border-collapse:collapse; +} + +.directory td { + background: #202020 !important; + margin: 0px; + padding: 0px; + vertical-align: top; +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; +} + +.directory td.entry a { + outline:none; +} + +.directory td.entry a img { + border: none; +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + padding-top: 3px; + border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory tr.even { + padding-left: 6px; + background-color: #202020; +} + +.directory img { + vertical-align: -30%; +} + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; +} + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #eab72c; +} + +.arrow { + color: #bb2222; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + width: 16px; + height: 22px; + box-sizing: content-box; +} + +.icon { + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #bb2222; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; +} + +.icona { + width: 24px; + height: 22px; + display: inline-block; +} + +.iconfopen { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('folderopen.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +.iconfclosed { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('folderclosed.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +.icondoc { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('doc.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +address { + font-style: normal; + color: #eab72c; +} + +table.doxtable caption { + caption-side: top; +} + +table.doxtable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +table.fieldtable { + /*width: 100%;*/ + margin-bottom: 10px; + border: 1px solid #101010; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + margin-left: auto; + margin-right: auto; +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #aaa; + border-bottom: 1px solid #aaa; + vertical-align: top; +} + +.fieldtable td.fieldname { + padding-top: 3px; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #aaa; + /*width: 100%;*/ +} + +.fieldtable td.fielddoc p:first-child { + margin-top: 0px; +} + +.fieldtable td.fielddoc p:last-child { + margin-bottom: 2px; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-color: #101010; + font-size: 100%; + color: #ddd; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #aaa; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-position: 0 -5px; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #101010; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li::before +{ + content: none !important; +} +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; + color: #eab72c; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.9); + text-decoration: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +table.classindex +{ + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; +} + +div.ingroups +{ + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +dl +{ + padding: 0 0 0 10px; +} + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ +dl.section +{ + margin-left: 0px; + padding-left: 0px; +} + +dl.note +{ + margin-left: 30px; + padding-left: 0.5em; + border-left:4px solid; + border-color: #bb0000; +} + +dl.warning, dl.attention +{ + margin-left: 30px; + padding-left: 3px; + border-left:4px solid; + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + margin-left: 30px; + padding-left: 3px; + border-left:4px solid; + border-color: #00D000; +} + +dl.deprecated +{ + margin-left: 30px; + padding-left: 3px; + border-left:4px solid; + border-color: #505050; +} + +dl.todo +{ + margin-left: 30px; + padding-left: 3px; + border-left:4px solid; + border-color: #00C0E0; +} + +dl.test +{ + margin-left: 30px; + padding-left: 3px; + border-left:4px solid; + border-color: #3030E0; +} + +dl.bug +{ + margin-left: 30px; + padding-left: 3px; + border-left:4px solid; + border-color: #C08050; +} + +dl.section dd { + margin-bottom: 6px; +} + + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectalign +{ + vertical-align: middle; +} + +#projectname +{ + font-size: 300%; + color: #ddd; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font-size: 50%; + color: #eab72c; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 15px; + width: 610px; + border-bottom: 15px solid #101010; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.plantumlgraph +{ + text-align: center; +} + +.diagraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +div.toc h3 { + font: bold 12px/1.2 Arial,FreeSans,sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.level2 { + margin-left: 15px; +} + +div.toc li.level3 { + margin-left: 30px; +} + +div.toc li.level4 { + margin-left: 45px; +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.inherit_header td { + padding: 6px 0px 2px 5px; +} + +.inherit { + display: none; +} + +tr.heading h2 { + margin-top: 12px; + margin-bottom: 4px; +} + +/* tooltip related style info */ + +.ttc { + position: absolute; + display: none; +} + +#powerTip { + cursor: default; + white-space: nowrap; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; +} + +#powerTip div.ttdoc { + color: grey; + font-style: italic; +} + +#powerTip div.ttname a { + font-weight: bold; +} + +#powerTip div.ttname { + font-weight: bold; +} + +#powerTip div.ttdeci { + color: #006318; +} + +#powerTip div { + margin: 0px; + padding: 0px; + font: 12px/16px sans-serif; +} + +#powerTip:before, #powerTip:after { + content: ""; + position: absolute; + margin: 0px; +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.s:after, #powerTip.s:before, +#powerTip.w:after, #powerTip.w:before, +#powerTip.e:after, #powerTip.e:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.nw:after, #powerTip.nw:before, +#powerTip.sw:after, #powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; +} + +#powerTip.n:after, #powerTip.s:after, +#powerTip.w:after, #powerTip.e:after, +#powerTip.nw:after, #powerTip.ne:after, +#powerTip.sw:after, #powerTip.se:after { + border-color: rgba(255, 255, 255, 0); +} + +#powerTip.n:before, #powerTip.s:before, +#powerTip.w:before, #powerTip.e:before, +#powerTip.nw:before, #powerTip.ne:before, +#powerTip.sw:before, #powerTip.se:before { + border-color: rgba(128, 128, 128, 0); +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.nw:after, #powerTip.nw:before { + top: 100%; +} + +#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { + border-top-color: #fff; + border-width: 10px; + margin: 0px -10px; +} +#powerTip.n:before { + border-top-color: #808080; + border-width: 11px; + margin: 0px -11px; +} +#powerTip.n:after, #powerTip.n:before { + left: 50%; +} + +#powerTip.nw:after, #powerTip.nw:before { + right: 14px; +} + +#powerTip.ne:after, #powerTip.ne:before { + left: 14px; +} + +#powerTip.s:after, #powerTip.s:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.sw:after, #powerTip.sw:before { + bottom: 100%; +} + +#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { + border-bottom-color: #ffffff; + border-width: 10px; + margin: 0px -10px; +} + +#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { + border-bottom-color: #808080; + border-width: 11px; + margin: 0px -11px; +} + +#powerTip.s:after, #powerTip.s:before { + left: 50%; +} + +#powerTip.sw:after, #powerTip.sw:before { + right: 14px; +} + +#powerTip.se:after, #powerTip.se:before { + left: 14px; +} + +#powerTip.e:after, #powerTip.e:before { + left: 100%; +} +#powerTip.e:after { + border-left-color: #ffffff; + border-width: 10px; + top: 50%; + margin-top: -10px; +} +#powerTip.e:before { + border-left-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +#powerTip.w:after, #powerTip.w:before { + right: 100%; +} +#powerTip.w:after { + border-right-color: #ffffff; + border-width: 10px; + top: 50%; + margin-top: -10px; +} +#powerTip.w:before { + border-right-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } +} + +/* @group Markdown */ + +/* +table.markdownTable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.markdownTableHead tr { +} + +table.markdownTableBodyLeft td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +th.markdownTableHeadLeft th.markdownTableHeadRight th.markdownTableHeadCenter th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +th.markdownTableHeadLeft { + text-align: left +} + +th.markdownTableHeadRight { + text-align: right +} + +th.markdownTableHeadCenter { + text-align: center +} +*/ + +table.markdownTable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.markdownTable tr { +} + +th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +th.markdownTableHeadLeft, td.markdownTableBodyLeft { + text-align: left +} + +th.markdownTableHeadRight, td.markdownTableBodyRight { + text-align: right +} + +th.markdownTableHeadCenter, td.markdownTableBodyCenter { + text-align: center +} + +/* template parameter formatting, ugh */ +.template_param_list { + display: table; +} +.template_expr div { + font-family: monospace, fixed; +} +.template_expr .template_decl { + float: left; + color: #aaa; +} +.template_template_param .template, .template_expr .default_argument .identifier +{ + float: left; +} +.template_template_param .type_decl { + padding-left: 1em; +} +.template_template_param .template_param_list .type_decl { + padding-left: 0; +} +.template_expr .default_argument .equals { + float: left; + padding-left: 0.5em; + padding-right: 0.5em; +} +.template_expr .default_argument { + float: left; +} + +.template_expr .open_bracket, .template_expr .comma, .template_expr .close_bracket { + float: left; + color: #bb2222; +} + +.template_expr .type_decl { + float: left; + color: #aaa; + padding-right: 0.5em; +} +.template_expr .identifier { + float: left; + color: #ddd; +} +.template_expr .close_bracket { + color: #bb2222; +} + +dl.author dt { + float: left; + font-weight: bold; + color: #ddd; + padding-right: 0.5em; +} + +code { + color: #ddd; + font-size: 85%; +} + +td.paramtype, td.memItemLeft, td.memTemplItemLeft, td.memItemRight +{ + position: relative; +} + +td.memItemLeft, td.memTemplItemLeft +{ + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; +} + +td.paramtype div.hover, td.memItemLeft div.hover, td.memTemplItemLeft div.hover, +td.memItemRight div.hover +{ + position: absolute; + top: -2px; + left: -2px; + display: none; + background: #303030; + color: #ddd; + padding-left: 3px; + padding-right: 3px; + font-family: monospace, fixed; + line-height: 1.1em; + margin: 0; + + white-space: normal; + overflow-wrap: break-word; + border: 2px solid #aaa; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; +} + +td.paramtype:hover div.hover, td.memItemLeft:hover div.hover, +td.memTemplItemLeft:hover div.hover, td.memItemRight:hover div.hover +{ + display: block; + z-index: 2; + overflow: visible; +} + +td.memItemRight:hover div.content, td.memItemLeft:hover div.content, +td.memTemplItemLeft:hover div.content +{ + display: none; +} + +td.memItemRight:hover div.hover, td.memItemRight div.hover, td.memItemLeft:hover +div.hover, td.memItemLeft div.hover, td.memTemplItemLeft:hover div.hover, +td.memTemplItemLeft div.hover +{ + position: relative !important; +} + +/* @end */ + +table.memname, table.memname tr, table.memname td, table.memname th +{ + background: #202020 !important; +} + +#nav-links +{ + margin-bottom: 0px !important; +} + +footer +{ + display: none; +} + +div#ens_header_version_select +{ + width: 180px; + color: #ddd; + border: 2px solid #333; + background: url('../../../res/menu_bg.png') no-repeat 96% 0; + height: 34px; + overflow: hidden; + margin-bottom: 2px; + border-radius: 5px; +} + +select.doxygen-version-select +{ + color: #ddd; + background: transparent; + border: none; + height: 29px; + padding: 5px; + width: 190px; +} diff --git a/_src/doxygen/dynamic_tables.js b/_src/doxygen/dynamic_tables.js new file mode 100644 index 000000000..9deb6f725 --- /dev/null +++ b/_src/doxygen/dynamic_tables.js @@ -0,0 +1,584 @@ +/** + * Utility function to return the last valid space we can break on, given HTML. + * We don't want to break inside of an HTML tag or inside a template argument. + */ +function lastValidSpaceIndex(html) +{ + var lastSpaceIndex = 0; + var inTag = false; + var templateDepth = 0; + for (var k = 0; k < html.length - 1; ++k) + { + if (html[k] === '<') + { + if (inTag) + { + console.log("Unexpected double tag at character " + k + "!"); + console.log(html); + } + inTag = true; + } + else if (html[k] === '>') + { + if (!inTag) + { + console.log("Unexpected tag close at character " + k + "!"); + console.log(html); + } + inTag = false; + } + else if (html[k] === '&' && !inTag) + { + if (k + 3 < html.length && + html.substring(k, k + 4) === '<') + { + templateDepth++; + } + else if (k + 3 < html.length && + html.substring(k, k + 4) === '>') + { + templateDepth--; + } + } + else if (html[k] === ' ' && templateDepth === 0 && !inTag) + { + // Seems like a valid space, take it. + lastSpaceIndex = k; + } + } + + return lastSpaceIndex; +} + +/** + * Given some HTML that contains a template declaration, parse that template + * declaration, formatting it in a series of divs so that it can be formatted + * properly. + * + * The keyword 'template' will go into a class="template_decl" div. + * The open-bracket character will go into a class="open_bracket" div. + * The close-bracket character will go into a class="close_bracket" div. + * The keyword 'typename' or 'class' will go into a class="type_decl" div. + * An identifier will go into a class="identifier" div. + * The keyword 'const' will go into a class="modifier" div. + * + * All of this will be put into a div with class="template_expr". + */ +function parseTemplateHTML(html) +{ + var inTag = false; + var lastSpaceIndex = 0; + var newDiv = document.createElement('div'); + newDiv.className = "template_decl"; + + for (var i = 0; i < html.length - 1; ++i) + { + // Basically we will just walk along looking for template arguments. + if (html[i] === '<') + { + if (inTag) + { + console.log("Unexpected double tag at character " + i + "!"); + console.log(html); + } + inTag = true; + } + else if (html[i] === '>') + { + if (!inTag) + { + console.log("Unexpected tag close at character " + i + "!"); + console.log(html); + } + inTag = false; + } + if (html[i] === '&' && !inTag) + { + if (i + 3 < html.length && + html.substring(i, i + 4) === '<') + { + // This is an opening declaration. + + // We have to handle the previous data. + var previousData = html.substring(lastSpaceIndex, i).trim(); + if (previousData === "class" || previousData === "typename") + { + var typeDeclDiv = document.createElement('div'); + typeDeclDiv.className = 'type_decl'; + typeDeclDiv.innerHTML = previousData; + newDiv.append(typeDeclDiv); + } + else if (previousData === "template") + { + var templateDeclDiv = document.createElement('div'); + templateDeclDiv.className = 'template_decl'; + templateDeclDiv.innerHTML = previousData; + newDiv.append(templateDeclDiv); + } + else if (previousData === "const") + { + var modifierDiv = document.createElement('div'); + modifierDiv.className = 'modifier'; + modifierDiv.innerHTML = previousData; + newDiv.append(modifierDiv); + } + else + { + // No clue what it is. + var identifierDiv = document.createElement('div'); + identifierDiv.className = 'identifier'; + identifierDiv.innerHTML = previousData; + newDiv.append(identifierDiv); + } + + // Create a div for opening the bracket. + var openingDiv = document.createElement('div'); + openingDiv.className = "open_bracket"; + openingDiv.innerHTML = '<'; + newDiv.append(openingDiv); + + i += 3; + lastSpaceIndex = i + 1; + } + else if (i + 3 < html.length && + html.substring(i, i + 4) === '>') + { + // This is a closing declaration. + + // We have to handle the previous data. + var previousData = html.substring(lastSpaceIndex, i).trim(); + if (previousData === "class" || previousData === "typename") + { + var typeDeclDiv = document.createElement('div'); + typeDeclDiv.className = 'type_decl'; + typeDeclDiv.innerHTML = previousData; + newDiv.append(typeDeclDiv); + } + else if (previousData === "template") + { + var templateDeclDiv = document.createElement('div'); + templateDeclDiv.className = 'template_decl'; + templateDeclDiv.innerHTML = previousData; + newDiv.append(templateDeclDiv); + } + else if (previousData === "const") + { + var modifierDiv = document.createElement('div'); + modifierDiv.className = 'modifier'; + modifierDiv.innerHTML = previousData; + newDiv.append(modifierDiv); + } + else + { + // No clue what it is. + var identifierDiv = document.createElement('div'); + identifierDiv.className = 'identifier'; + identifierDiv.innerHTML = previousData; + newDiv.append(identifierDiv); + } + + // Now create the closing div. + var closingDiv = document.createElement('div'); + closingDiv.className = 'close_bracket'; + closingDiv.innerHTML = '>'; + newDiv.append(closingDiv); + + i += 3; + lastSpaceIndex = i + 1; + } + } + if (html[i] === ' ' && !inTag) + { + // Check if we need to do anything. + var previousData = html.substring(lastSpaceIndex, i).trim(); + if (previousData === "class" || previousData === "typename") + { + var typeDeclDiv = document.createElement('div'); + typeDeclDiv.className = 'type_decl'; + typeDeclDiv.innerHTML = previousData; + newDiv.append(typeDeclDiv); + } + else if (previousData === "template") + { + var templateDeclDiv = document.createElement('div'); + templateDeclDiv.className = 'template_decl'; + templateDeclDiv.innerHTML = previousData; + newDiv.append(templateDeclDiv); + } + else if (previousData === "const") + { + var modifierDiv = document.createElement('div'); + modifierDiv.className = 'modifier'; + modifierDiv.innerHTML = previousData; + newDiv.append(modifierDiv); + } + else + { + continue; // Nothing to do, keep reading. + } + + lastSpaceIndex = i; + } + } + + previousData = html.substring(lastSpaceIndex); + + // Get the last data into the last div. + if (previousData === "class" || previousData === "typename") + { + var typeDeclDiv = document.createElement('div'); + typeDeclDiv.className = 'type_decl'; + typeDeclDiv.innerHTML = previousData; + newDiv.append(typeDeclDiv); + } + else if (previousData === "template") + { + var templateDeclDiv = document.createElement('div'); + templateDeclDiv.className = 'template_decl'; + templateDeclDiv.innerHTML = previousData; + newDiv.append(templateDeclDiv); + } + else if (previousData === "const") + { + var modifierDiv = document.createElement('div'); + modifierDiv.className = 'modifier'; + modifierDiv.innerHTML = previousData; + newDiv.append(modifierDiv); + } + else + { + var lastDiv = document.createElement('div'); + lastDiv.className = 'identifier'; + lastDiv.innerHTML = previousData; + newDiv.append(lastDiv); + } + + return newDiv; +} + +// When we load the page, go through each of the tables and make sure that the +// given width is such that the table is readable. +function resizeTables(splitLines) +{ + // We want to modify tables of class 'memname'. We want to do the following: + // + // 1. strip namespace information into separate line if needed + // 2. make sure the td with the name of the function is wide enough to fit it + // 3. make sure the td with the '(' and ')' is exactly as wide as it needs to + // be + // 4. make sure that the parameter names are all sufficiently long + // 5. use any extra space for the parameter types, with mouseover showing the + // full type + // + // But what if there is no extra space at all? Then we have to put the + // function name on a separate line and make it a three-column table. + var tables = document.querySelectorAll('table.memname'); + if (tables !== null) + { + var contentsDiv = document.querySelectorAll('div.contents')[0]; + // 61 pixels are used for margins on the memitem divs. + var maxWidth = contentsDiv.offsetWidth - 61; + for (var i = 0; i < tables.length; i++) + { + // Now we are looking at tables[i]. Loop over each of its child's + // children (which will be s), in order to find the maximum widths of + // each. + var trs = tables[i].children[0].children; + // These will hold the maximum current widths. + var nameWidth = 0, parenWidth = 0, paramTypeWidth = 0, paramNameWidth = 0, + secondParenWidth = 0, qualifierWidth = 0; + for (var j = 0; j < trs.length; j++) + { + // Now each tr will have four tds only. + var tds = trs[j].children; + if (tds.length === 4) + { + nameWidth = Math.max(nameWidth, tds[0].clientWidth); + parenWidth = Math.max(parenWidth, tds[1].clientWidth); + paramTypeWidth = Math.max(paramTypeWidth, tds[2].clientWidth); + paramNameWidth = Math.max(paramNameWidth, tds[3].clientWidth); + } + else if (tds.length === 5) + { + // This method has a qualifier. + nameWidth = Math.max(nameWidth, tds[0].clientWidth); + parenWidth = Math.max(parenWidth, tds[1].clientWidth); + paramTypeWidth = Math.max(paramTypeWidth, tds[2].clientWidth); + paramNameWidth = Math.max(paramNameWidth, tds[3].clientWidth); + qualifierWidth = Math.max(qualifierWidth, tds[4].clientWidth); + } + else if (tds.length === 6) + { + // This declaration is all on one line. + nameWidth = Math.max(nameWidth, tds[0].clientWidth); + parenWidth = Math.max(parenWidth, tds[1].clientWidth); + paramTypeWidth = Math.max(paramTypeWidth, tds[2].clientWidth); + paramNameWidth = Math.max(paramNameWidth, tds[3].clientWidth); + secondParenWidth = Math.max(secondParenWidth, tds[4].clientWidth); + qualifierWidth = Math.max(qualifierWidth, tds[5].clientWidth); + } + else if (tds.length === 1) + { + // This is a member declaration with only one column. + nameWidth = Math.max(nameWidth, tds[0].clientWidth); + } + + // Compute name width manually. + } + var computedParamTypeWidth = maxWidth - (nameWidth + parenWidth + + paramNameWidth + secondParenWidth + qualifierWidth + 1); +// console.log("computedParamTypeWidth " + computedParamTypeWidth + " maxWidth " + maxWidth + " nameWidth " + nameWidth + " parenWidth " + parenWidth +//+ " paramNameWidth " + paramNameWidth + " secondParenWidth " + secondParenWidth +//+ " qualifiedWidth " + qualifierWidth + " paramTypeWidth " + paramTypeWidth); + + // If we have lots of pixels left over, I don't see the need to push the + // parameter names all the way to the right (it could make it hard to + // read). So add a max margin of 50px to the paramNameWidth. + computedParamTypeWidth = + Math.min(paramTypeWidth + 50, computedParamTypeWidth); + + if (paramTypeWidth > 150 && + computedParamTypeWidth <= 150 && + splitLines === true) + { + // Special action needed: try reducing the length of the name of the + // method. First try wrapping on a space. + + // Remove any extraneous whitespace from the beginning and end of the + // HTML... + trs[0].children[0].innerHTML = trs[0].children[0].innerHTML.trim(); + + var name = trs[0].children[0].innerText; +// console.log("function '" + name + "' is too wide, len " + trs[0].children[0].innerHTML.length + "!"); + + // Find the last space that is not in a tag or part of a template + // expression. We ignore the last character because it may be padding. + var lastSpaceIndex = lastValidSpaceIndex(trs[0].children[0].innerHTML); + + // Attempt to split at the last space. + var newInnerHTML = trs[0].children[0].innerHTML.substring(lastSpaceIndex); + var outsideInnerHTML = trs[0].children[0].innerHTML.substring(0, lastSpaceIndex); + + // Now set the HTML in the name parameter to only have the function + // name, and then add a new TR to the parent that spans all columns. + trs[0].children[0].innerHTML = newInnerHTML; + nameWidth = trs[0].children[0].clientWidth; + + var newTr = document.createElement("tr"); + var newTd = document.createElement("td"); + newTd.innerHTML = outsideInnerHTML; + newTd.colSpan = (secondParenWidth > 0 || qualifierWidth > 0) ? 6 : 4; + newTr.append(newTd); + + // Recompute the paramName width. + computedParamNameWidth = Math.max(10, maxWidth - (nameWidth + parenWidth + + paramNameWidth + secondParenWidth + qualifierWidth)); + + if (computedParamNameWidth > 150) + { + tables[i].children[0].insertBefore(newTr, trs[0]); + } + else + { + // Round two: try putting the namespace/class on its own separate tr, then + // the parameters on their own lines. + + var secondNewTr = document.createElement("tr"); + var secondNewTd = document.createElement("td"); + + // Find the index to split on the last namespace/class (::). + var lastIndex = newInnerHTML.lastIndexOf(':'); + secondNewTd.innerHTML = newInnerHTML.substr(0, lastIndex); + trs[0].children[0].innerHTML = newInnerHTML.substr(lastIndex + 1); + secondNewTd.colSpan = (secondParenWidth > 0 || qualifierWidth > 0) ? 6 : 4; + secondNewTr.append(secondNewTd); + + tables[i].children[0].insertBefore(secondNewTr, trs[0]); + tables[i].children[0].insertBefore(newTr, secondNewTr); + + // Is the second new td too wide? + if (secondNewTd.clientWidth <= 580) + { + nameWidth = 100; /* force to 100px */ + computedParamTypeWidth = Math.max(10, maxWidth - (nameWidth + + parenWidth + paramNameWidth + secondParenWidth + qualifierWidth)); + } + else + { + console.log("line too long!"); + computedParamTypeWidth = Math.max(10, maxWidth - (nameWidth + + parenWidth + paramNameWidth + secondParenWidth + qualifierWidth)); + } + } + } + + // Now that we know the width of the elements, set the widths + // preferentially, by looping over all the trs again. + for (var j = 0; j < trs.length; j++) + { + var tds = trs[j].children; + if (tds.length === 4) + { + tds[0].style.width = nameWidth + "px"; + tds[1].style.width = parenWidth + "px"; + tds[2].style.width = computedParamTypeWidth + "px"; + tds[2].style.maxWidth = tds[2].style.width; + tds[2].style.textOverflow = "ellipsis"; + tds[2].style.overflow = "hidden"; + tds[3].style.width = paramNameWidth + "px"; + } + else if (tds.length == 6) + { + tds[0].style.width = nameWidth + "px"; + tds[1].style.width = parenWidth + "px"; + tds[2].style.width = computedParamTypeWidth + "px"; + tds[2].style.maxWidth = tds[2].style.width; + tds[2].style.textOverflow = "ellipsis"; + tds[2].style.overflow = "hidden"; + tds[3].style.width = paramNameWidth + "px"; + tds[4].style.width = secondParenWidth + "px"; + tds[5].style.width = qualifierWidth + "px"; + } + } + + // If it is six columns, we have to make sure that the following + // mlabels-right has the float: right attribute. The mlabels-right td is + // the last child of the parent of the parent table. + if (secondParenWidth > 0 || qualifierWidth > 0) + { + var mlabelsright = tables[i].parentElement.parentElement.children[1]; + mlabelsright.style.cssFloat = "right"; + } + } + } + + // Force td.memItemLeft and td.memTemplItemLeft tables to have custom style. + var tds = document.querySelectorAll('td.memItemRight, td.memItemLeft, td.memTemplItemLeft'); + for (i = 0; i < tds.length; ++i) + { + if (tds[i].offsetWidth < tds[i].scrollWidth) + { + // Put all the content in a div. + var contentDiv = document.createElement('div'); + if (tds[i].classList.contains("memItemRight")) + contentDiv.style.cssText = 'white-space: nowrap; text-overflow: ellipsis; overflow: hidden; color: #ddd; text-align: left;'; + else + contentDiv.style.cssText = 'white-space: nowrap; text-overflow: ellipsis; overflow: hidden; color: #ddd; text-align: left; width: 150px;'; + contentDiv.innerHTML = tds[i].innerHTML.trim(); + contentDiv.className = 'content'; + + tds[i].innerHTML = ''; + tds[i].style.cssText = ''; + tds[i].append(contentDiv); + + var hoverDiv = document.createElement('div'); + hoverDiv.className = 'hover'; + hoverDiv.innerHTML = tds[i].children[0].innerHTML.trim(); + + // Make it not too wide for the page. So, get the width of the rest of + // the table. + var tr = tds[i].parentElement; + var found = false; + var width = 0; + for (j = 0; j < tr.children.length; j++) + { + if (tr.children[j] === tds[i]) + found = true; + + if (found == true) + { + width = width + tr.children[j].offsetWidth; + } + } + + hoverDiv.style.minWidth = tds[i].offsetWidth; + hoverDiv.style.maxWidth = width; + + tds[i].append(hoverDiv); + } + } + + // Next, make sure that tooltips show on hover. + var paramtypes = document.querySelectorAll('td.paramtype'); + for (i = 0; i < paramtypes.length; ++i) + { + if (paramtypes[i].offsetWidth < paramtypes[i].scrollWidth) + { + // We need to add a tooltip, but we also have to put the existing stuff + // into its own styled div. + var contentDiv = document.createElement('div'); + contentDiv.style.cssText = paramtypes[i].style.cssText; + contentDiv.innerHTML = paramtypes[i].innerHTML; + contentDiv.className = 'content'; + + var hoverDiv = document.createElement('div'); + hoverDiv.className = 'hover'; + hoverDiv.innerHTML = paramtypes[i].innerHTML; + + // Make it not too wide for the page. So, get the width of the rest of + // the table. + var tr = paramtypes[i].parentElement; + var found = false; + var width = 0; + for (j = 0; j < tr.children.length; j++) + { + if (tr.children[j] === paramtypes[i]) + found = true; + + if (found == true) + { + width = width + tr.children[j].offsetWidth; + } + } + + hoverDiv.style.minWidth = paramtypes[i].offsetWidth; + hoverDiv.style.maxWidth = width; + + paramtypes[i].innerHTML = ''; + paramtypes[i].style.cssText = ''; + paramtypes[i].append(contentDiv); + paramtypes[i].append(hoverDiv); + } + } +} + +// Clear any custom formatting from tables. +function clearTableSizes() +{ + var tds = document.querySelectorAll('td.memItemRight, td.memItemLeft, td.memTemplItemLeft, td.paramtype'); + for (var k = 0; k < tds.length; k++) + { + // First, check if this has a hover div in it. In this case we have + // to pull out the content div and delete the hover div. + if (tds[k].children.length == 2) + { + if (tds[k].children[0].classList.contains("content") && + tds[k].children[1].classList.contains("hover")) + { + var content = tds[k].children[0].innerHTML; + while (tds[k].firstChild) + tds[k].removeChild(tds[k].firstChild); + + tds[k].innerHTML = content; + } + } + tds[k].style.cssText = null; + } +} + +var resizeTimeout; +function throttleResize() +{ + if (!resizeTimeout) + { + resizeTimeout = setTimeout(function() + { + resizeTimeout = null; + clearTableSizes(); + resizeTables(false); + }, 66); + } +} + +window.onload = function() { resizeTables(true); }; +window.addEventListener("resize", throttleResize, false); diff --git a/_src/doxygen/footer.html b/_src/doxygen/footer.html new file mode 100644 index 000000000..5b17b47f7 --- /dev/null +++ b/_src/doxygen/footer.html @@ -0,0 +1,22 @@ + + + + + + +
+ + + + + + + + diff --git a/_src/doxygen/footer_old.html b/_src/doxygen/footer_old.html new file mode 100644 index 000000000..29aa94ff5 --- /dev/null +++ b/_src/doxygen/footer_old.html @@ -0,0 +1,17 @@ + + + + + + diff --git a/_src/doxygen/header.3.0.0.html b/_src/doxygen/header.3.0.0.html new file mode 100644 index 000000000..c99d45099 --- /dev/null +++ b/_src/doxygen/header.3.0.0.html @@ -0,0 +1,267 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
ensmallen
+
+
mlpack
fast, +flexible C++ machine learning library
+
+
+ + + + + +
+ diff --git a/_src/doxygen/header.3.0.1.html b/_src/doxygen/header.3.0.1.html new file mode 100644 index 000000000..c4ff8221a --- /dev/null +++ b/_src/doxygen/header.3.0.1.html @@ -0,0 +1,267 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
ensmallen
+
+
mlpack
fast, +flexible C++ machine learning library
+
+
+ + + + + +
+ diff --git a/_src/doxygen/header.3.0.2.html b/_src/doxygen/header.3.0.2.html new file mode 100644 index 000000000..e0f246900 --- /dev/null +++ b/_src/doxygen/header.3.0.2.html @@ -0,0 +1,267 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
ensmallen
+
+
mlpack
fast, +flexible C++ machine learning library
+
+
+ + + + + +
+ diff --git a/_src/doxygen/header.3.0.3.html b/_src/doxygen/header.3.0.3.html new file mode 100644 index 000000000..c15e84207 --- /dev/null +++ b/_src/doxygen/header.3.0.3.html @@ -0,0 +1,267 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
ensmallen
+
+
mlpack
fast, +flexible C++ machine learning library
+
+
+ + + + + +
+ diff --git a/_src/doxygen/header.3.0.4.html b/_src/doxygen/header.3.0.4.html new file mode 100644 index 000000000..db446ebea --- /dev/null +++ b/_src/doxygen/header.3.0.4.html @@ -0,0 +1,267 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
ensmallen
+
+
mlpack
fast, +flexible C++ machine learning library
+
+
+ + + + + +
+ diff --git a/_src/doxygen/header.git.html b/_src/doxygen/header.git.html new file mode 100644 index 000000000..ef4e516bd --- /dev/null +++ b/_src/doxygen/header.git.html @@ -0,0 +1,259 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
ensmallen
+
+
mlpack
fast, +flexible C++ machine learning library
+
+
+ + + + + +
+ diff --git a/_src/doxygen/header.html b/_src/doxygen/header.html new file mode 100644 index 000000000..ddffc7102 --- /dev/null +++ b/_src/doxygen/header.html @@ -0,0 +1,115 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+ + ensmallen + +
+
+
+ mlpack
fast, flexible C++ machine learning library
+
+
+ +
+
+
+ + + +
+
diff --git a/_src/doxygen/header.template.html b/_src/doxygen/header.template.html new file mode 100644 index 000000000..4ecaf1f66 --- /dev/null +++ b/_src/doxygen/header.template.html @@ -0,0 +1,45 @@ + + + + + + + + + +mlpack: mlpack Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_src/doxygen/header_old.html b/_src/doxygen/header_old.html new file mode 100644 index 000000000..37dd4559a --- /dev/null +++ b/_src/doxygen/header_old.html @@ -0,0 +1,55 @@ + + + + + + +mlpack: a scalable c++ machine learning library + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ + + + + + +
+
$projectname +  $projectnumber +
+
+
diff --git a/_src/doxygen/htaccess.template b/_src/doxygen/htaccess.template new file mode 100644 index 000000000..aa982fa1e --- /dev/null +++ b/_src/doxygen/htaccess.template @@ -0,0 +1 @@ +ErrorDocument 404 404.html diff --git a/_src/doxygen/html_template_labeler.py b/_src/doxygen/html_template_labeler.py new file mode 100644 index 000000000..fc65e2b11 --- /dev/null +++ b/_src/doxygen/html_template_labeler.py @@ -0,0 +1,65 @@ +from __future__ import print_function +from HTMLParser import HTMLParser +from template_annotator import TemplateAnnotator +from xml.sax.saxutils import escape + +import sys + +class HTMLTemplateLabeler(HTMLParser): + current_data = '' + + html_escape_table = { + '"': """, + "'": "'" + } + + def html_escape(self, text): + return escape(text, self.html_escape_table) + + def handle_starttag(self, tag, attrs): + # Print the tag. Is it a br? Then we can ignore it. + if tag == "br": + print("
", end='') + return + elif tag == "link": + print("", end='') + return + + # Process the current data we have. + if self.current_data != '': + self.process_data() + + print("<" + tag + " " + ' '.join([a[0] + '="' + self.html_escape(a[1]) + '"' for a in attrs]) + ">", end='') + self.current_data = '' + + def handle_endtag(self, tag): + # Print the tag, after processing the data we currently have. + self.process_data() + + # Print the closing tag. + print ("", end='') + self.current_data = '' + + # HTMLParser splits up the escaped HTML characters, so we have to reassemble + # by having an internal data storage buffer. + def handle_data(self, data): + self.current_data = self.current_data + data + + def handle_entityref(self, name): + self.current_data = self.current_data + "&" + name + ";" + + def handle_charref(self, name): + self.current_data = self.current_data + "&#" + name + ";" + + def process_data(self): + # Do nothing with empty data. + if self.current_data == '': + return + + # See if it matches the template grammar. + t = TemplateAnnotator() + processed = t.process(self.current_data) # Will modify, if it matches. + + # Print the data. + print(processed, end='') + diff --git a/_src/doxygen/html_template_labeler.pyc b/_src/doxygen/html_template_labeler.pyc new file mode 100644 index 000000000..add864136 Binary files /dev/null and b/_src/doxygen/html_template_labeler.pyc differ diff --git a/_src/doxygen/label_html_templates.py b/_src/doxygen/label_html_templates.py new file mode 100755 index 000000000..0f918782a --- /dev/null +++ b/_src/doxygen/label_html_templates.py @@ -0,0 +1,10 @@ +#!/usr/bin/python +# +# Process the given HTML file, outputting the labeled templates on stdout. +import sys +from html_template_labeler import HTMLTemplateLabeler + +labeler = HTMLTemplateLabeler() + +f = open(sys.argv[1], 'r') +labeler.feed(f.read()) diff --git a/_src/doxygen/msearchbox.html b/_src/doxygen/msearchbox.html new file mode 100644 index 000000000..2563bf7f3 --- /dev/null +++ b/_src/doxygen/msearchbox.html @@ -0,0 +1,15 @@ +
+ + + + + + + +
diff --git a/_src/doxygen/navtree.css b/_src/doxygen/navtree.css new file mode 100644 index 000000000..03fd6e4bf --- /dev/null +++ b/_src/doxygen/navtree.css @@ -0,0 +1,154 @@ +#nav-tree .children_ul { + margin:0; + padding:4px; +} + +#nav-tree ul { + list-style:none outside none; + margin:0px; + padding:0px; +} + +#nav-tree li { + white-space:nowrap; + margin:0px; + padding:0px; +} + +#nav-tree li::before { + content:""; +} + +#nav-tree .plus { + margin:0px; +} + +#nav-tree .selected { + background-image: url('tab_a.png'); + background-repeat:repeat-x; + color: #000; +} + +#nav-tree img { + margin:0px; + padding:0px; + border:0px; + vertical-align: middle; +} + +#nav-tree a { + text-decoration:none; + padding:0px; + margin:0px; + outline:none; +} + +#nav-tree .label { + margin:0px; + padding:0px; + font: 12px 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; +} + +#nav-tree .label a { + padding:2px; + color: #eab72c; +} + +#nav-tree .label a:hover { + color: #bb0000; +} + +#nav-tree .selected a { + text-decoration:bold; + color:#000; +} + +#nav-tree .children_ul { + margin:0px; + padding:0px; +} + +#nav-tree .item { + margin:0px; + padding:0px; +} + +#nav-tree { + padding: 0px 0px; + background-color: #FAFAFF; + font-size:14px; + overflow:auto; +} + +#doc-content { + overflow:auto; + display:block; + padding:0px; + margin:0px; + -webkit-overflow-scrolling : touch; /* iOS 5+ */ +} + +#side-nav { + padding:0 6px 0 0; + margin: 0px; + display:block; + position: absolute; + left: 0px; + width: 220px; +} + +.ui-resizable .ui-resizable-handle { + display:block; +} + +.ui-resizable-e { + background-image:url("splitbar.png"); + background-size:100%; + background-repeat:repeat-y; + background-attachment: scroll; + cursor:ew-resize; + height:100%; + right:0; + top:0; + width:6px; +} + +.ui-resizable-handle { + display:none; + font-size:0.1px; + position:absolute; + z-index:1; +} + +#nav-tree-contents { + margin: 6px 0px 0px 0px; +} + +#nav-tree { + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #000000; + -webkit-overflow-scrolling : touch; /* iOS 5+ */ +} + +#nav-sync { + position:absolute; + top:5px; + right:24px; + z-index:0; +} + +#nav-sync img { + opacity:0.3; +} + +#nav-sync img:hover { + opacity:0.9; +} + +@media print +{ + #nav-tree { display: none; } + div.ui-resizable-handle { display: none; position: relative; } +} + diff --git a/_src/doxygen/navtree.js b/_src/doxygen/navtree.js new file mode 100644 index 000000000..d2cda6d1e --- /dev/null +++ b/_src/doxygen/navtree.js @@ -0,0 +1,518 @@ +var navTreeSubIndices = new Array(); +var arrowDown = '▼'; +var arrowRight = '►'; + +function getData(varName) +{ + var i = varName.lastIndexOf('/'); + var n = i>=0 ? varName.substring(i+1) : varName; + return eval(n.replace(/\-/g,'_')); +} + +function stripPath(uri) +{ + return uri.substring(uri.lastIndexOf('/')+1); +} + +function stripPath2(uri) +{ + var i = uri.lastIndexOf('/'); + var s = uri.substring(i+1); + var m = uri.substring(0,i+1).match(/\/d\w\/d\w\w\/$/); + return m ? uri.substring(i-6) : s; +} + +function hashValue() +{ + return $(location).attr('hash').substring(1).replace(/[^\w\-]/g,''); +} + +function hashUrl() +{ + return '#'+hashValue(); +} + +function pathName() +{ + return $(location).attr('pathname').replace(/[^-A-Za-z0-9+&@#/%?=~_|!:,.;\(\)]/g, ''); +} + +function localStorageSupported() +{ + try { + return 'localStorage' in window && window['localStorage'] !== null && window.localStorage.getItem; + } + catch(e) { + return false; + } +} + + +function storeLink(link) +{ + if (!$("#nav-sync").hasClass('sync') && localStorageSupported()) { + window.localStorage.setItem('navpath',link); + } +} + +function deleteLink() +{ + if (localStorageSupported()) { + window.localStorage.setItem('navpath',''); + } +} + +function cachedLink() +{ + if (localStorageSupported()) { + return window.localStorage.getItem('navpath'); + } else { + return ''; + } +} + +function getScript(scriptName,func,show) +{ + var head = document.getElementsByTagName("head")[0]; + var script = document.createElement('script'); + script.id = scriptName; + script.type = 'text/javascript'; + script.onload = func; + script.src = scriptName+'.js'; + if ($.browser.msie && $.browser.version<=8) { + // script.onload does not work with older versions of IE + script.onreadystatechange = function() { + if (script.readyState=='complete' || script.readyState=='loaded') { + func(); if (show) showRoot(); + } + } + } + head.appendChild(script); +} + +function createIndent(o,domNode,node,level) +{ + var level=-1; + var n = node; + while (n.parentNode) { level++; n=n.parentNode; } + if (node.childrenData) { + var imgNode = document.createElement("span"); + imgNode.className = 'arrow'; + imgNode.style.paddingLeft=(16*level).toString()+'px'; + imgNode.innerHTML=arrowRight; + node.plus_img = imgNode; + node.expandToggle = document.createElement("a"); + node.expandToggle.href = "javascript:void(0)"; + node.expandToggle.onclick = function() { + if (node.expanded) { + $(node.getChildrenUL()).slideUp("fast"); + node.plus_img.innerHTML=arrowRight; + node.expanded = false; + } else { + expandNode(o, node, false, false); + } + } + node.expandToggle.appendChild(imgNode); + domNode.appendChild(node.expandToggle); + } else { + var span = document.createElement("span"); + span.className = 'arrow'; + span.style.width = 16*(level+1)+'px'; + span.innerHTML = ' '; + domNode.appendChild(span); + } +} + +var animationInProgress = false; + +function gotoAnchor(anchor,aname,updateLocation) +{ + var pos, docContent = $('#doc-content'); + var ancParent = $(anchor.parent()); + if (ancParent.hasClass('memItemLeft') || + ancParent.hasClass('fieldname') || + ancParent.hasClass('fieldtype') || + ancParent.is(':header')) + { + pos = ancParent.position().top; + } else if (anchor.position()) { + pos = anchor.position().top; + } + if (pos) { + var dist = Math.abs(Math.min( + pos-docContent.offset().top, + docContent[0].scrollHeight- + docContent.height()-docContent.scrollTop())); + animationInProgress=true; + docContent.animate({ + scrollTop: pos + docContent.scrollTop() - docContent.offset().top + },Math.max(50,Math.min(500,dist)),function(){ + if (updateLocation) window.location.href=aname; + animationInProgress=false; + }); + } +} + +function newNode(o, po, text, link, childrenData, lastNode) +{ + var node = new Object(); + node.children = Array(); + node.childrenData = childrenData; + node.depth = po.depth + 1; + node.relpath = po.relpath; + node.isLast = lastNode; + + node.li = document.createElement("li"); + po.getChildrenUL().appendChild(node.li); + node.parentNode = po; + + node.itemDiv = document.createElement("div"); + node.itemDiv.className = "item"; + + node.labelSpan = document.createElement("span"); + node.labelSpan.className = "label"; + + createIndent(o,node.itemDiv,node,0); + node.itemDiv.appendChild(node.labelSpan); + node.li.appendChild(node.itemDiv); + + var a = document.createElement("a"); + node.labelSpan.appendChild(a); + node.label = document.createTextNode(text); + node.expanded = false; + a.appendChild(node.label); + if (link) { + var url; + if (link.substring(0,1)=='^') { + url = link.substring(1); + link = url; + } else { + url = node.relpath+link; + } + a.className = stripPath(link.replace('#',':')); + if (link.indexOf('#')!=-1) { + var aname = '#'+link.split('#')[1]; + var srcPage = stripPath(pathName()); + var targetPage = stripPath(link.split('#')[0]); + a.href = srcPage!=targetPage ? url : "javascript:void(0)"; + a.onclick = function(){ + storeLink(link); + if (!$(a).parent().parent().hasClass('selected')) + { + $('.item').removeClass('selected'); + $('.item').removeAttr('id'); + $(a).parent().parent().addClass('selected'); + $(a).parent().parent().attr('id','selected'); + } + var anchor = $(aname); + gotoAnchor(anchor,aname,true); + }; + } else { + a.href = url; + a.onclick = function() { storeLink(link); } + } + } else { + if (childrenData != null) + { + a.className = "nolink"; + a.href = "javascript:void(0)"; + a.onclick = node.expandToggle.onclick; + } + } + + node.childrenUL = null; + node.getChildrenUL = function() { + if (!node.childrenUL) { + node.childrenUL = document.createElement("ul"); + node.childrenUL.className = "children_ul"; + node.childrenUL.style.display = "none"; + node.li.appendChild(node.childrenUL); + } + return node.childrenUL; + }; + + return node; +} + +function showRoot() +{ + var headerHeight = $("#top").height() + $("#ens_header").height() + + $("#nav_links").height(); + var footerHeight = $("#nav-path").height(); + var windowHeight = $(window).height() - headerHeight - footerHeight; + (function (){ // retry until we can scroll to the selected item + try { + var navtree=$('#nav-tree'); + navtree.scrollTo('#selected',0,{offset:-windowHeight/2}); + } catch (err) { + setTimeout(arguments.callee, 0); + } + })(); +} + +function expandNode(o, node, imm, showRoot) +{ + if (node.childrenData && !node.expanded) { + if (typeof(node.childrenData)==='string') { + var varName = node.childrenData; + getScript(node.relpath+varName,function(){ + node.childrenData = getData(varName); + expandNode(o, node, imm, showRoot); + }, showRoot); + } else { + if (!node.childrenVisited) { + getNode(o, node); + } if (imm || ($.browser.msie && $.browser.version>8)) { + // somehow slideDown jumps to the start of tree for IE9 :-( + $(node.getChildrenUL()).show(); + } else { + $(node.getChildrenUL()).slideDown("fast"); + } + node.plus_img.innerHTML = arrowDown; + node.expanded = true; + } + } +} + +function glowEffect(n,duration) +{ + n.addClass('glow').delay(duration).queue(function(next){ + $(this).removeClass('glow');next(); + }); +} + +function highlightAnchor() +{ + var aname = hashUrl(); + var anchor = $(aname); + if (anchor.parent().attr('class')=='memItemLeft'){ + var rows = $('.memberdecls tr[class$="'+hashValue()+'"]'); + glowEffect(rows.children(),300); // member without details + } else if (anchor.parent().attr('class')=='fieldname'){ + glowEffect(anchor.parent().parent(),1000); // enum value + } else if (anchor.parent().attr('class')=='fieldtype'){ + glowEffect(anchor.parent().parent(),1000); // struct field + } else if (anchor.parent().is(":header")) { + glowEffect(anchor.parent(),1000); // section header + } else { + glowEffect(anchor.next(),1000); // normal member + } + gotoAnchor(anchor,aname,false); +} + +function selectAndHighlight(hash,n) +{ + var a; + if (hash) { + var link=stripPath(pathName())+':'+hash.substring(1); + a=$('.item a[class$="'+link+'"]'); + } + if (a && a.length) { + a.parent().parent().addClass('selected'); + a.parent().parent().attr('id','selected'); + highlightAnchor(); + } else if (n) { + $(n.itemDiv).addClass('selected'); + $(n.itemDiv).attr('id','selected'); + } + if ($('#nav-tree-contents .item:first').hasClass('selected')) { + $('#nav-sync').css('top','30px'); + } else { + $('#nav-sync').css('top','5px'); + } + showRoot(); +} + +function showNode(o, node, index, hash) +{ + if (node && node.childrenData) { + if (typeof(node.childrenData)==='string') { + var varName = node.childrenData; + getScript(node.relpath+varName,function(){ + node.childrenData = getData(varName); + showNode(o,node,index,hash); + },true); + } else { + if (!node.childrenVisited) { + getNode(o, node); + } + $(node.getChildrenUL()).css({'display':'block'}); + node.plus_img.innerHTML = arrowDown; + node.expanded = true; + var n = node.children[o.breadcrumbs[index]]; + if (index+11) hash = '#'+parts[1].replace(/[^\w\-]/g,''); + else hash=''; + } + if (hash.match(/^#l\d+$/)) { + var anchor=$('a[name='+hash.substring(1)+']'); + glowEffect(anchor.parent(),1000); // line number + hash=''; // strip line number anchors + } + var url=root+hash; + var i=-1; + while (NAVTREEINDEX[i+1]<=url) i++; + if (i==-1) { i=0; root=NAVTREE[0][1]; } // fallback: show index + if (navTreeSubIndices[i]) { + gotoNode(o,i,root,hash,relpath) + } else { + getScript(relpath+'navtreeindex'+i,function(){ + navTreeSubIndices[i] = eval('NAVTREEINDEX'+i); + if (navTreeSubIndices[i]) { + gotoNode(o,i,root,hash,relpath); + } + },true); + } +} + +function showSyncOff(n,relpath) +{ + n.html(''); +} + +function showSyncOn(n,relpath) +{ + n.html(''); +} + +function toggleSyncButton(relpath) +{ + var navSync = $('#nav-sync'); + if (navSync.hasClass('sync')) { + navSync.removeClass('sync'); + showSyncOff(navSync,relpath); + storeLink(stripPath2(pathName())+hashUrl()); + } else { + navSync.addClass('sync'); + showSyncOn(navSync,relpath); + deleteLink(); + } +} + +function initNavTree(toroot,relpath) +{ + var o = new Object(); + o.toroot = toroot; + o.node = new Object(); + o.node.li = document.getElementById("nav-tree-contents"); + o.node.childrenData = NAVTREE; + o.node.children = new Array(); + o.node.childrenUL = document.createElement("ul"); + o.node.getChildrenUL = function() { return o.node.childrenUL; }; + o.node.li.appendChild(o.node.childrenUL); + o.node.depth = 0; + o.node.relpath = relpath; + o.node.expanded = false; + o.node.isLast = true; + o.node.plus_img = document.createElement("span"); + o.node.plus_img.className = 'arrow'; + o.node.plus_img.innerHTML = arrowRight; + + if (localStorageSupported()) { + var navSync = $('#nav-sync'); + if (cachedLink()) { + showSyncOff(navSync,relpath); + navSync.removeClass('sync'); + } else { + showSyncOn(navSync,relpath); + } + navSync.click(function(){ toggleSyncButton(relpath); }); + } + + $(window).load(function(){ + navTo(o,toroot,hashUrl(),relpath); + showRoot(); + }); + + $(window).bind('hashchange', function(){ + if (window.location.hash && window.location.hash.length>1){ + var a; + if ($(location).attr('hash')){ + var clslink=stripPath(pathName())+':'+hashValue(); + a=$('.item a[class$="'+clslink.replace(/''' + +jsAddition = ''' + + + + + + +''' + +visPattern = "

" + +visAddition = ''' +

This visualization allows us to see how many popular optimizers perform on different optimization problems. Select a problem to optimize, then select an optimizer and tune its parameters, and see the steps that the optimizer takes plotted in pink. Note that you can zoom in and rotate the drawing. Also, you can compare how different optimizers perform on a given problem in the second graph. As you try a given problem with more optimizers, the objective function vs. the number of iterations is plotted for each optimizer you have tried.

+ +


















Step-size: 0.01


Iterations: 4000

The defaults here are not necessarily good for the given problem, so it is suggested that the values used be tailored to the task at hand. (Use the mouse to zoom/drag the function.)

First moment coefficient: 0.9

Second moment coefficient: 0.999

Second moment coefficient: 0.999

Second moment coefficient: 0.999

As intuition says, system has higher probability of staying in the states with a smaller stepsize. As the stepsize goes up, imbalance becomes stronger. When the stepsize is close to zero, the system stays in the state(s) with the highest cost.


Evaluations: Dynamic


Unique Optimizer
Restrict Evaluations

A plot of the cost reveals distinct properties for each optimizer with its own style of convergence.

+

+Optimizer

+''' + +p = content.find(jsPattern) + +if p > 0: + content = content[0:p+len(jsPattern)] + "\n" + jsAddition + "\n" + content[p+len(jsPattern):len(content)] + + p = content.find(visPattern) + if p > 0: + content = content[0:p+len(visPattern)] + "\n" + visAddition + "\n" + content[p+len(visPattern):len(content)] + + with open(optimizerVisualizationFile, "w") as f: + f.write(content) diff --git a/_src/doxygen/resize.js b/_src/doxygen/resize.js new file mode 100644 index 000000000..56e4a023c --- /dev/null +++ b/_src/doxygen/resize.js @@ -0,0 +1,114 @@ +function initResizable() +{ + var cookie_namespace = 'doxygen'; + var sidenav,navtree,content,header,collapsed,collapsedWidth=0,barWidth=6,desktop_vp=768,titleHeight; + + function readCookie(cookie) + { + var myCookie = cookie_namespace+"_"+cookie+"="; + if (document.cookie) { + var index = document.cookie.indexOf(myCookie); + if (index != -1) { + var valStart = index + myCookie.length; + var valEnd = document.cookie.indexOf(";", valStart); + if (valEnd == -1) { + valEnd = document.cookie.length; + } + var val = document.cookie.substring(valStart, valEnd); + return val; + } + } + return 0; + } + + function writeCookie(cookie, val, expiration) + { + if (val==undefined) return; + if (expiration == null) { + var date = new Date(); + date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week + expiration = date.toGMTString(); + } + document.cookie = cookie_namespace + "_" + cookie + "=" + val + "; expires=" + expiration+"; path=/"; + } + + function resizeWidth() + { + var windowWidth = $(window).width() + "px"; + var sidenavWidth = $(sidenav).outerWidth(); + content.css({marginLeft:parseInt(sidenavWidth)+"px"}); + writeCookie('width',sidenavWidth-barWidth, null); + } + + function restoreWidth(navWidth) + { + var windowWidth = $(window).width() + "px"; + content.css({marginLeft:parseInt(navWidth)+barWidth+"px"}); + sidenav.css({width:navWidth + "px"}); + } + + function resizeHeight() + { + var headerHeight = header.outerHeight(); + var footerHeight = footer.outerHeight(); + var windowHeight = $(window).height() - headerHeight - footerHeight; + content.css({height:windowHeight + "px"}); + navtree.css({height:windowHeight + "px"}); + sidenav.css({height:windowHeight + "px"}); + var width=$(window).width(); + if (width!=collapsedWidth) { + if (width=desktop_vp) { + if (!collapsed) { + collapseExpand(); + } + } else if (width>desktop_vp && collapsedWidth0) { + restoreWidth(0); + collapsed=true; + } + else { + var width = readCookie('width'); + if (width>200 && width<$(window).width()) { restoreWidth(width); } else { restoreWidth(200); } + collapsed=false; + } + } + + header = $("#top"); + sidenav = $("#side-nav"); + content = $("#doc-content"); + navtree = $("#nav-tree"); + footer = $("#nav-path"); + $(".side-nav-resizable").resizable({resize: function(e, ui) { resizeWidth(); } }); + $(sidenav).resizable({ minWidth: 0 }); + $(window).resize(function() { resizeHeight(); }); + var device = navigator.userAgent.toLowerCase(); + var touch_device = device.match(/(iphone|ipod|ipad|android)/); + if (touch_device) { /* wider split bar for touch only devices */ + $(sidenav).css({ paddingRight:'20px' }); + $('.ui-resizable-e').css({ width:'20px' }); + $('#nav-sync').css({ right:'34px' }); + barWidth=20; + } + var width = readCookie('width'); + if (width) { restoreWidth(width); } else { resizeWidth(); } + resizeHeight(); + var url = location.href; + var i=url.indexOf("#"); + if (i>=0) window.location.hash=url.substr(i); + var _preventDefault = function(evt) { evt.preventDefault(); }; + $("#splitbar").bind("dragstart", _preventDefault).bind("selectstart", _preventDefault); + $(".ui-resizable-handle").dblclick(collapseExpand); + $(window).load(resizeHeight); +} + + diff --git a/_src/doxygen/style-doxygen.css b/_src/doxygen/style-doxygen.css new file mode 100644 index 000000000..22057738e --- /dev/null +++ b/_src/doxygen/style-doxygen.css @@ -0,0 +1,50 @@ +/* lists +ul li +{ + color: #aaaaaa; + font-weight: normal; + list-style: none; +} + +ul li::before { + content: "• "; + color: #eab72c; +} + +li a +{ + color: #eab72c; + font-weight: normal; +} + +li a.el +{ + font-weight: normal; +} + +li a:hover +{ + color: #ffffff !important; + font-weight: normal; +}*/ + +p.footnote +{ + border-top: 15px solid #101010; + padding-top: 1em; +} + +.code +{ + font-family: monospace, fixed; +} + +li code a +{ + font-size: 85%; +} + +div.title +{ + display: none; +} diff --git a/_src/doxygen/tabs.css b/_src/doxygen/tabs.css new file mode 100644 index 000000000..e6b0ca150 --- /dev/null +++ b/_src/doxygen/tabs.css @@ -0,0 +1,473 @@ +.sm { + position: relative; + z-index: 9999 +} + +.sm, +.sm ul, +.sm li { + display: block; + list-style: none; + margin: 0; + padding: 0; + line-height: normal; + direction: ltr; + text-align: left; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0) +} + +.sm-rtl, +.sm-rtl ul, +.sm-rtl li { + direction: rtl; + text-align: right +} + +.sm>li>h1, +.sm>li>h2, +.sm>li>h3, +.sm>li>h4, +.sm>li>h5, +.sm>li>h6 { + margin: 0; + padding: 0 +} + +.sm ul { + display: none +} + +.sm li, +.sm a { + position: relative +} + +.sm a { + display: block +} + +.sm a.disabled { + cursor: not-allowed +} + +.sm:after { + content: "\00a0"; + display: block; + height: 0; + font: 0/0 serif; + clear: both; + visibility: hidden; + overflow: hidden +} + +.sm, +.sm *, +.sm *:before, +.sm *:after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box +} + +.sm-dox { + background-image: url("tab_b.png") +} + +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:hover, +.sm-dox a:active { + padding: 0 12px; + padding-right: 43px; + font-family: "Lucida Grande", "Geneva", "Helvetica", Arial, sans-serif; + font-size: 13px; + font-weight: bold; + line-height: 36px; + text-decoration: none; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.9); + color: #ffffff !important; + outline: 0 +} + +.sm-dox a:hover { + background-image: url("tab_a.png"); + background-repeat: repeat-x; + color: white; + text-shadow: 0 1px 1px black +} + +.sm-dox a.current { + color: #d23600 +} + +.sm-dox a.disabled { + color: #bbb +} + +.sm-dox a span.sub-arrow { + position: absolute; + top: 50%; + margin-top: -14px; + left: auto; + right: 3px; + width: 28px; + height: 28px; + overflow: hidden; + font: bold 12px/28px monospace!important; + text-align: center; + text-shadow: none; + background: rgba(255, 255, 255, 0.5); + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px +} + +.sm-dox a.highlighted span.sub-arrow:before { + display: block; + content: '-' +} + +.sm-dox>li:first-child>a, +.sm-dox>li:first-child>:not(ul) a { + -moz-border-radius: 5px 5px 0 0; + -webkit-border-radius: 5px; + border-radius: 5px 5px 0 0 +} + +.sm-dox>li:last-child>a, +.sm-dox>li:last-child>*:not(ul) a, +.sm-dox>li:last-child>ul, +.sm-dox>li:last-child>ul>li:last-child>a, +.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a, +.sm-dox>li:last-child>ul>li:last-child>ul, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul { + -moz-border-radius: 0 0 5px 5px; + -webkit-border-radius: 0; + border-radius: 0 0 5px 5px +} + +.sm-dox>li:last-child>a.highlighted, +.sm-dox>li:last-child>*:not(ul) a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted, +.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted { + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0 +} + +.sm-dox ul { + background: rgba(162, 162, 162, 0.1) +} + +.sm-dox ul a, +.sm-dox ul a:focus, +.sm-dox ul a:hover, +.sm-dox ul a:active { + font-size: 12px; + border-left: 8px solid transparent; + line-height: 24px; + text-shadow: none; + background-color: #000; + background-image: none +} + +.sm-dox ul a:hover { + background-image: url("tab_a.png"); + background-repeat: repeat-x; + color: white; + text-shadow: 0 1px 1px black +} + +.sm-dox ul ul a, +.sm-dox ul ul a:hover, +.sm-dox ul ul a:focus, +.sm-dox ul ul a:active { + border-left: 16px solid transparent +} + +.sm-dox ul ul ul a, +.sm-dox ul ul ul a:hover, +.sm-dox ul ul ul a:focus, +.sm-dox ul ul ul a:active { + border-left: 24px solid transparent +} + +.sm-dox ul ul ul ul a, +.sm-dox ul ul ul ul a:hover, +.sm-dox ul ul ul ul a:focus, +.sm-dox ul ul ul ul a:active { + border-left: 32px solid transparent +} + +.sm-dox ul ul ul ul ul a, +.sm-dox ul ul ul ul ul a:hover, +.sm-dox ul ul ul ul ul a:focus, +.sm-dox ul ul ul ul ul a:active { + border-left: 40px solid transparent +} + +@media(min-width:768px) { + .sm-dox ul { + position: absolute; + width: 12em + } + .sm-dox li { + float: left + } + .sm-dox.sm-rtl li { + float: right + } + .sm-dox ul li, + .sm-dox.sm-rtl ul li, + .sm-dox.sm-vertical li { + float: none + } + .sm-dox a { + white-space: nowrap + } + .sm-dox ul a, + .sm-dox.sm-vertical a { + white-space: normal + } + .sm-dox .sm-nowrap>li>a, + .sm-dox .sm-nowrap>li>:not(ul) a { + white-space: nowrap + } + .sm-dox { + padding: 0 10px; + background: #222222; + line-height: 36px; + width: 610px + } + .sm-dox a span.sub-arrow { + top: 50%; + margin-top: -2px; + right: 12px; + width: 0; + height: 0; + border-width: 4px; + border-style: solid dashed dashed dashed; + border-color: #ffffff transparent transparent transparent; + background: transparent; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0 + } + .sm-dox a, + .sm-dox a:focus, + .sm-dox a:active, + .sm-dox a:hover, + .sm-dox a.highlighted { + padding: 0 12px; + background-image: url("tab_s.png"); + background-repeat: no-repeat; + background-position: right; + -moz-border-radius: 0!important; + -webkit-border-radius: 0; + border-radius: 0!important + } + .sm-dox a:hover { + background-image: url("tab_a.png"); + background-repeat: repeat-x; + color: white; + text-shadow: 0 1px 1px black + } + .sm-dox a:hover span.sub-arrow { + border-color: white transparent transparent transparent + } + .sm-dox a.has-submenu { + padding-right: 24px + } + .sm-dox li { + border-top: 0 + } + .sm-dox li::before { + content: none; + } + .sm-dox>li>ul:before, + .sm-dox>li>ul:after { + content: ''; + position: absolute; + top: -18px; + left: 30px; + width: 0; + height: 0; + overflow: hidden; + border-width: 9px; + border-style: dashed dashed solid dashed; + border-color: transparent transparent #000 transparent + } + .sm-dox>li>ul:after { + top: -16px; + left: 31px; + border-width: 8px; + border-color: transparent transparent #000 transparent + } + .sm-dox ul { + border: 1px solid #222; + padding: 5px 0; + background: #000; + -moz-border-radius: 5px!important; + -webkit-border-radius: 5px; + border-radius: 5px!important; + -moz-box-shadow: 0 5px 9px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 5px 9px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 9px rgba(0, 0, 0, 0.2) + } + .sm-dox ul a span.sub-arrow { + right: 8px; + top: 50%; + margin-top: -5px; + border-width: 5px; + border-color: transparent transparent transparent #fff; + border-style: dashed dashed dashed solid + } + .sm-dox ul a, + .sm-dox ul a:hover, + .sm-dox ul a:focus, + .sm-dox ul a:active, + .sm-dox ul a.highlighted { + color: #555; + background-image: none; + border: 0!important; + color: #555; + background-image: none + } + .sm-dox ul a:hover { + background-image: url("tab_a.png"); + background-repeat: repeat-x; + color: white; + text-shadow: 0 1px 1px black + } + .sm-dox ul a:hover span.sub-arrow { + border-color: transparent transparent transparent white + } + .sm-dox span.scroll-up, + .sm-dox span.scroll-down { + position: absolute; + display: none; + visibility: hidden; + overflow: hidden; + background: #fff; + height: 36px + } + .sm-dox span.scroll-up:hover, + .sm-dox span.scroll-down:hover { + background: #eee + } + .sm-dox span.scroll-up:hover span.scroll-up-arrow, + .sm-dox span.scroll-up:hover span.scroll-down-arrow { + border-color: transparent transparent #d23600 transparent + } + .sm-dox span.scroll-down:hover span.scroll-down-arrow { + border-color: #d23600 transparent transparent transparent + } + .sm-dox span.scroll-up-arrow, + .sm-dox span.scroll-down-arrow { + position: absolute; + top: 0; + left: 50%; + margin-left: -6px; + width: 0; + height: 0; + overflow: hidden; + border-width: 6px; + border-style: dashed dashed solid dashed; + border-color: transparent transparent #555 transparent + } + .sm-dox span.scroll-down-arrow { + top: 8px; + border-style: solid dashed dashed dashed; + border-color: #555 transparent transparent transparent + } + .sm-dox.sm-rtl a.has-submenu { + padding-right: 12px; + padding-left: 24px + } + .sm-dox.sm-rtl a span.sub-arrow { + right: auto; + left: 12px + } + .sm-dox.sm-rtl.sm-vertical a.has-submenu { + padding: 10px 20px + } + .sm-dox.sm-rtl.sm-vertical a span.sub-arrow { + right: auto; + left: 8px; + border-style: dashed solid dashed dashed; + border-color: transparent #555 transparent transparent + } + .sm-dox.sm-rtl>li>ul:before { + left: auto; + right: 30px + } + .sm-dox.sm-rtl>li>ul:after { + left: auto; + right: 31px + } + .sm-dox.sm-rtl ul a.has-submenu { + padding: 10px 20px!important + } + .sm-dox.sm-rtl ul a span.sub-arrow { + right: auto; + left: 8px; + border-style: dashed solid dashed dashed; + border-color: transparent #555 transparent transparent + } + .sm-dox.sm-vertical { + padding: 10px 0; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px + } + .sm-dox.sm-vertical a { + padding: 10px 20px + } + .sm-dox.sm-vertical a:hover, + .sm-dox.sm-vertical a:focus, + .sm-dox.sm-vertical a:active, + .sm-dox.sm-vertical a.highlighted { + background: #fff + } + .sm-dox.sm-vertical a.disabled { + background-image: url("tab_b.png") + } + .sm-dox.sm-vertical a span.sub-arrow { + right: 8px; + top: 50%; + margin-top: -5px; + border-width: 5px; + border-style: dashed dashed dashed solid; + border-color: transparent transparent transparent #555 + } + .sm-dox.sm-vertical>li>ul:before, + .sm-dox.sm-vertical>li>ul:after { + display: none + } + .sm-dox.sm-vertical ul a { + padding: 10px 20px + } + .sm-dox.sm-vertical ul a:hover, + .sm-dox.sm-vertical ul a:focus, + .sm-dox.sm-vertical ul a:active, + .sm-dox.sm-vertical ul a.highlighted { + background: #eee + } + .sm-dox.sm-vertical ul a.disabled { + background: #fff + } +} diff --git a/_src/doxygen/template_annotator.py b/_src/doxygen/template_annotator.py new file mode 100644 index 000000000..3410b9f96 --- /dev/null +++ b/_src/doxygen/template_annotator.py @@ -0,0 +1,104 @@ +from pyparsing import Literal, Word, Combine, Group, Optional, ZeroOrMore, \ + Forward, nums, alphas, alphanums, LineEnd, ParseException, Suppress, \ + OneOrMore, delimitedList +import re +import sys + +class TemplateAnnotator: + + def __init__(self): + + # Define the grammar. We need some literals. + templateLiteral = Literal("template")("template") + openBracket = Literal("<")("open_bracket") + closeBracket = Literal(">")("close_bracket") + typenameLiteral = Literal("typename")("type_decl") + classLiteral = Literal("class")("type_decl") + equalLiteral = Literal("=") + comma = Literal(",")("comma") + ellipses = Literal("...") + operatorLiteral = Literal("operator") + tildeLiteral = Literal("~") + + digit = Word(nums) + + identifier_nondigit = Word(alphas + "_:") # catch underscores and :: + + identifier = Forward() + identifier << (identifier_nondigit + Optional(identifier) + Optional(digit))("identifier*") + + default_argument = Group(Literal("=")("equals") + identifier)("default_argument*") + + template_param_list = Forward() + + template_param = Group(classLiteral + Optional(identifier) + Optional(default_argument))("template_param*") |\ + Group(typenameLiteral + Optional(identifier) + Optional(default_argument))("template_param*") |\ + Group(templateLiteral + openBracket + template_param_list + closeBracket + classLiteral + Optional(identifier) + Optional(default_argument))("template_template_param*") + + # This needs to be its own separate thing so that the comma gets grouped in + # the template_param div. + template_param_with_comma = Group(classLiteral + Optional(identifier) + Optional(default_argument) + comma)("template_param*") |\ + Group(typenameLiteral + Optional(identifier) + Optional(default_argument) + comma)("template_param*") |\ + Group(templateLiteral + openBracket + template_param_list + Group(classLiteral + Optional(identifier) + Optional(default_argument) + comma)("template_template_param_name"))("template_template_param*") + + # This also needs to be its own separate thing so that the closing bracket + # gets grouped in the template_param div. + template_param_with_close = Group(classLiteral + Optional(identifier) + Optional(default_argument) + closeBracket)("template_param*") |\ + Group(typenameLiteral + Optional(identifier) + Optional(default_argument) + closeBracket)("template_param*") |\ + Group(templateLiteral + openBracket + template_param_list + Group(classLiteral + Optional(identifier) + Optional(default_argument) + closeBracket)("template_template_param_name"))("template_template_param") + + template_param_list << Group(ZeroOrMore(template_param_with_comma) + template_param_with_close)("template_param_list") + + self.grammar = (Literal("template")("template_decl") + openBracket + template_param_list) |\ + (Literal("template")("template_decl") + openBracket + closeBracket) + + def process(self, string): + try: + a = self.grammar.parseString(string) + + # Now process the string. We get XML back, but we really need named divs. + retstr = a.asXML('template_expr')\ + .replace(' + %(author)s + ''' + +rawTutorialFiles = glob.glob(rawTutorialPath + "*/*.txt") + +for file in rawTutorialFiles: + with open(file) as f: content = f.read() + + # Extract all author lines. It's possible that a page has multiple authors, + # in this case the syntax should look like: + # @author Author1 + # @author Author2 + authorLines = [line for line in content.split('\n') if "@author" in line] + + # Remove '@author' from the line. + for i in range(len(authorLines)): + authorLines[i] = authorLines[i].replace("@author", "") + + authorBlock = '''
'''; + + # Construct the author html block. + for authorContent in authorLines: + # Extract additional author information if provided. The syntax looks + # like: @author Author1 (information). + infoPattern = re.compile(r'.* \((.*)\)', re.MULTILINE|re.DOTALL) + info = infoPattern.match(authorContent) + info = info.groups()[0] if info else "" + + if info == "": + author = authorContent.strip() + else: + author = authorContent[0:authorContent.find('(')].strip() + + if authorBlock == '''
''': + author = "Author: " + author + + authorBlock += authorHTML % {'author' : author, 'info' : info} + + authorBlock += '''
''' + + # Extract the page name. + pagePattern = re.compile(r'.*@page (\w+)', re.MULTILINE|re.DOTALL) + page = pagePattern.match(content) + page = page.groups()[0] if page else None + + # Search for the doxygen generated page. + if page: + tutorial = glob.glob(doxygenTutorialPath + page + ".html") + + if len(tutorial) == 1: + with open(tutorial[0]) as f: content = f.read() + + # Add the author information right after the first headline. + h = content.find('') + if h > 0: + with open(tutorial[0], "w") as f: + f.write(content[0:h+5]) + f.write(authorBlock) + f.write(content[h+6:-1]) diff --git a/_src/doxygen/version_redirect.js b/_src/doxygen/version_redirect.js new file mode 100644 index 000000000..6f1766b90 --- /dev/null +++ b/_src/doxygen/version_redirect.js @@ -0,0 +1,10 @@ +function changeVersion() +{ + // Get the current URL, and redirect accordingly. + var currentLocation = window.location.href; + // Get selection. + version = document.getElementById("version-select").value; + + window.location.href = + currentLocation.replace(/\/mlpack-[^\/]*/, '/mlpack-' + version); +} diff --git a/_src/markdown-patches/menu_bg.png b/_src/markdown-patches/menu_bg.png new file mode 100644 index 000000000..c29580a40 Binary files /dev/null and b/_src/markdown-patches/menu_bg.png differ diff --git a/_src/markdown-patches/mlpack-3.0.0-markdown.patch b/_src/markdown-patches/mlpack-3.0.0-markdown.patch new file mode 100644 index 000000000..dd2bfc700 --- /dev/null +++ b/_src/markdown-patches/mlpack-3.0.0-markdown.patch @@ -0,0 +1,7187 @@ +From ea96cfaa9470ce1a6e8d51a22ae7c464ee0ed124 Mon Sep 17 00:00:00 2001 +From: Ryan Curtin +Date: Wed, 6 Mar 2019 19:56:30 -0500 +Subject: [PATCH] Backport Markdown support onto mlpack 3.0.0. + +--- + CMake/RunProgram.cmake | 8 + + CMakeLists.txt | 14 + + src/mlpack/CMakeLists.txt | 9 +- + src/mlpack/bindings/CMakeLists.txt | 2 + + src/mlpack/bindings/cli/CMakeLists.txt | 2 + + .../bindings/cli/default_param_impl.hpp | 59 +- + src/mlpack/bindings/cli/end_program.hpp | 2 +- + .../bindings/cli/get_printable_type.hpp | 79 +++ + .../bindings/cli/get_printable_type_impl.hpp | 109 ++++ + .../bindings/cli/print_doc_functions.hpp | 38 ++ + .../bindings/cli/print_doc_functions_impl.hpp | 110 +++- + src/mlpack/bindings/cli/print_help.cpp | 6 +- + src/mlpack/bindings/cli/print_type_doc.hpp | 81 +++ + .../bindings/cli/print_type_doc_impl.hpp | 173 ++++++ + src/mlpack/bindings/cli/string_type_param.hpp | 6 - + .../bindings/cli/string_type_param_impl.hpp | 10 - + src/mlpack/bindings/markdown/CMakeLists.txt | 209 +++++++ + .../markdown/MarkdownCategories.cmake | 11 + + src/mlpack/bindings/markdown/binding_info.cpp | 49 ++ + src/mlpack/bindings/markdown/binding_info.hpp | 62 ++ + .../bindings/markdown/default_param.hpp | 45 ++ + .../markdown/generate_markdown.binding.cpp.in | 46 ++ + .../markdown/generate_markdown.binding.hpp.in | 28 + + .../markdown/generate_markdown.cpp.in | 114 ++++ + .../bindings/markdown/get_binding_name.cpp | 40 ++ + .../bindings/markdown/get_binding_name.hpp | 30 + + src/mlpack/bindings/markdown/get_param.hpp | 38 ++ + .../bindings/markdown/get_printable_param.hpp | 126 ++++ + .../markdown/get_printable_param_name.hpp | 83 +++ + .../get_printable_param_name_impl.hpp | 79 +++ + .../markdown/get_printable_param_value.hpp | 88 +++ + .../get_printable_param_value_impl.hpp | 84 +++ + .../bindings/markdown/get_printable_type.hpp | 62 ++ + .../bindings/markdown/is_serializable.hpp | 63 ++ + src/mlpack/bindings/markdown/md_option.hpp | 109 ++++ + .../bindings/markdown/print_doc_functions.hpp | 109 ++++ + .../markdown/print_doc_functions_impl.hpp | 540 ++++++++++++++++++ + src/mlpack/bindings/markdown/print_docs.cpp | 218 +++++++ + src/mlpack/bindings/markdown/print_docs.hpp | 33 ++ + .../bindings/markdown/print_type_doc.hpp | 46 ++ + .../bindings/markdown/program_doc_wrapper.hpp | 41 ++ + .../bindings/markdown/res/change_language.js | 102 ++++ + .../bindings/markdown/res/formatting.css | 142 +++++ + src/mlpack/bindings/markdown/res/menu_bg.png | Bin 0 -> 395 bytes + src/mlpack/bindings/python/CMakeLists.txt | 4 + + src/mlpack/bindings/python/default_param.hpp | 95 +++ + .../bindings/python/default_param_impl.hpp | 147 +++++ + .../bindings/python/get_cython_type.hpp | 10 - + .../bindings/python/get_printable_type.hpp | 120 ++++ + .../python/get_printable_type_impl.hpp | 151 +++++ + src/mlpack/bindings/python/print_doc.hpp | 22 +- + .../bindings/python/print_doc_functions.hpp | 26 + + .../python/print_doc_functions_impl.hpp | 124 +++- + .../python/print_input_processing.hpp | 1 - + src/mlpack/bindings/python/print_type_doc.hpp | 81 +++ + .../bindings/python/print_type_doc_impl.hpp | 162 ++++++ + src/mlpack/bindings/python/py_option.hpp | 4 + + .../python/tests/test_python_binding_main.cpp | 1 + + src/mlpack/core/util/cli.cpp | 3 +- + src/mlpack/core/util/mlpack_main.hpp | 68 ++- + src/mlpack/core/util/param.hpp | 31 +- + src/mlpack/core/util/program_doc.cpp | 30 +- + src/mlpack/core/util/program_doc.hpp | 17 +- + src/mlpack/methods/CMakeLists.txt | 4 + + src/mlpack/methods/adaboost/CMakeLists.txt | 1 + + src/mlpack/methods/adaboost/adaboost_main.cpp | 21 +- + src/mlpack/methods/approx_kfn/CMakeLists.txt | 1 + + .../methods/approx_kfn/approx_kfn_main.cpp | 19 +- + src/mlpack/methods/cf/CMakeLists.txt | 1 + + src/mlpack/methods/cf/cf_main.cpp | 25 +- + src/mlpack/methods/dbscan/CMakeLists.txt | 1 + + src/mlpack/methods/dbscan/dbscan_main.cpp | 12 +- + .../methods/decision_stump/CMakeLists.txt | 1 + + .../decision_stump/decision_stump_main.cpp | 12 +- + .../methods/decision_tree/CMakeLists.txt | 1 + + .../decision_tree/decision_tree_main.cpp | 16 +- + src/mlpack/methods/det/CMakeLists.txt | 1 + + src/mlpack/methods/det/det_main.cpp | 15 +- + src/mlpack/methods/emst/CMakeLists.txt | 1 + + src/mlpack/methods/emst/emst_main.cpp | 13 +- + src/mlpack/methods/fastmks/CMakeLists.txt | 1 + + src/mlpack/methods/fastmks/fastmks_main.cpp | 15 +- + src/mlpack/methods/gmm/CMakeLists.txt | 5 + + src/mlpack/methods/gmm/gmm_generate_main.cpp | 12 +- + .../methods/gmm/gmm_probability_main.cpp | 13 +- + src/mlpack/methods/gmm/gmm_train_main.cpp | 13 +- + src/mlpack/methods/hmm/CMakeLists.txt | 7 + + src/mlpack/methods/hmm/hmm_generate_main.cpp | 18 +- + src/mlpack/methods/hmm/hmm_loglik_main.cpp | 19 +- + src/mlpack/methods/hmm/hmm_train_main.cpp | 21 +- + src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 19 +- + .../methods/hoeffding_trees/CMakeLists.txt | 1 + + .../hoeffding_trees/hoeffding_tree_main.cpp | 14 +- + src/mlpack/methods/kernel_pca/CMakeLists.txt | 1 + + .../methods/kernel_pca/kernel_pca_main.cpp | 17 +- + src/mlpack/methods/kmeans/CMakeLists.txt | 1 + + src/mlpack/methods/kmeans/kmeans_main.cpp | 32 +- + src/mlpack/methods/lars/CMakeLists.txt | 1 + + src/mlpack/methods/lars/lars_main.cpp | 21 +- + .../methods/linear_regression/CMakeLists.txt | 1 + + .../linear_regression_main.cpp | 14 +- + .../local_coordinate_coding/CMakeLists.txt | 1 + + .../local_coordinate_coding_main.cpp | 14 +- + .../logistic_regression/CMakeLists.txt | 1 + + .../logistic_regression_main.cpp | 17 +- + src/mlpack/methods/lsh/CMakeLists.txt | 1 + + src/mlpack/methods/lsh/lsh_main.cpp | 17 +- + src/mlpack/methods/mean_shift/CMakeLists.txt | 1 + + .../methods/mean_shift/mean_shift_main.cpp | 23 +- + src/mlpack/methods/naive_bayes/CMakeLists.txt | 1 + + src/mlpack/methods/naive_bayes/nbc_main.cpp | 14 +- + src/mlpack/methods/nca/CMakeLists.txt | 1 + + src/mlpack/methods/nca/nca_main.cpp | 15 +- + .../methods/neighbor_search/CMakeLists.txt | 3 + + .../methods/neighbor_search/kfn_main.cpp | 14 +- + .../methods/neighbor_search/knn_main.cpp | 17 +- + src/mlpack/methods/nmf/CMakeLists.txt | 1 + + src/mlpack/methods/nmf/nmf_main.cpp | 25 +- + src/mlpack/methods/pca/CMakeLists.txt | 1 + + src/mlpack/methods/pca/pca_main.cpp | 24 +- + src/mlpack/methods/perceptron/CMakeLists.txt | 1 + + .../methods/perceptron/perceptron_main.cpp | 13 +- + src/mlpack/methods/preprocess/CMakeLists.txt | 7 + + .../preprocess/preprocess_binarize_main.cpp | 13 +- + .../preprocess/preprocess_describe_main.cpp | 22 +- + .../preprocess/preprocess_imputer_main.cpp | 15 +- + .../preprocess/preprocess_split_main.cpp | 20 +- + src/mlpack/methods/radical/CMakeLists.txt | 1 + + src/mlpack/methods/radical/radical_main.cpp | 26 +- + .../methods/random_forest/CMakeLists.txt | 1 + + .../random_forest/random_forest_main.cpp | 17 +- + .../methods/range_search/CMakeLists.txt | 1 + + .../range_search/range_search_main.cpp | 16 +- + src/mlpack/methods/rann/CMakeLists.txt | 1 + + src/mlpack/methods/rann/krann_main.cpp | 17 +- + .../methods/softmax_regression/CMakeLists.txt | 1 + + .../softmax_regression_main.cpp | 26 +- + .../methods/sparse_coding/CMakeLists.txt | 1 + + .../sparse_coding/sparse_coding_main.cpp | 33 +- + 139 files changed, 5011 insertions(+), 205 deletions(-) + create mode 100644 CMake/RunProgram.cmake + create mode 100644 src/mlpack/bindings/cli/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/cli/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/CMakeLists.txt + create mode 100644 src/mlpack/bindings/markdown/MarkdownCategories.cmake + create mode 100644 src/mlpack/bindings/markdown/binding_info.cpp + create mode 100644 src/mlpack/bindings/markdown/binding_info.hpp + create mode 100644 src/mlpack/bindings/markdown/default_param.hpp + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.cpp.in + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.cpp + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/markdown/is_serializable.hpp + create mode 100644 src/mlpack/bindings/markdown/md_option.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.cpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.hpp + create mode 100644 src/mlpack/bindings/markdown/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/markdown/program_doc_wrapper.hpp + create mode 100644 src/mlpack/bindings/markdown/res/change_language.js + create mode 100644 src/mlpack/bindings/markdown/res/formatting.css + create mode 100644 src/mlpack/bindings/markdown/res/menu_bg.png + create mode 100644 src/mlpack/bindings/python/default_param.hpp + create mode 100644 src/mlpack/bindings/python/default_param_impl.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc_impl.hpp + +diff --git a/CMake/RunProgram.cmake b/CMake/RunProgram.cmake +new file mode 100644 +index 000000000..6ae385314 +--- /dev/null ++++ b/CMake/RunProgram.cmake +@@ -0,0 +1,8 @@ ++# RunProgram.cmake: a CMake script that actually runs the given program to ++# generate a file, which is output into the given directory. ++# ++# This script depends on the following arguments: ++# ++# PROGRAM: the program to run to. ++# OUTPUT_FILE: the file to store the output in. ++execute_process(COMMAND ${PROGRAM} OUTPUT_FILE ${OUTPUT_FILE}) +diff --git a/CMakeLists.txt b/CMakeLists.txt +index d49f466d2..07e9293ae 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -15,6 +15,20 @@ option(BUILD_CLI_EXECUTABLES "Build command-line executables." ON) + option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) + option(BUILD_SHARED_LIBS + "Compile shared libraries (if OFF, static libraries are compiled)." ON) ++ ++# Currently Python bindings aren't known to build successfully on Windows, so ++# set BUILD_PYTHON_BINDINGS to OFF when the platform is Windows. ++if (WIN32) ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." OFF) ++ message(WARNING "By default Python bindings are not compiled for Windows because they are not known to work. Set BUILD_PYTHON_BINDINGS to ON if you want them built.") ++else () ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) ++endif() ++ ++# Build Markdown bindings for documentation. This is used as part of website ++# generation. ++option(BUILD_MARKDOWN_BINDINGS "Build Markdown bindings for website documentation." OFF) ++ + option(BUILD_WITH_COVERAGE + "Build with support for code coverage tools (gcc only)." OFF) + option(MATHJAX +diff --git a/src/mlpack/CMakeLists.txt b/src/mlpack/CMakeLists.txt +index 29c545c9e..b555e0100 100644 +--- a/src/mlpack/CMakeLists.txt ++++ b/src/mlpack/CMakeLists.txt +@@ -14,7 +14,7 @@ set(DIRS + ) + + foreach(dir ${DIRS}) +- add_subdirectory(${dir}) ++ add_subdirectory(${dir}) + endforeach() + + # MLPACK_SRCS is set in the subdirectories. The dependencies (MLPACK_LIBRARIES) +@@ -40,8 +40,7 @@ if (NOT BUILD_SHARED_LIBS) + add_definitions(-DMLPACK_STATIC_DEFINE) + endif () + +-target_link_libraries(mlpack +- ${MLPACK_LIBRARIES}) ++target_link_libraries(mlpack ${MLPACK_LIBRARIES}) + + set_target_properties(mlpack + PROPERTIES +@@ -122,3 +121,7 @@ if (BUILD_PYTHON_BINDINGS) + configure_file(${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/setup.py.in + ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py) + endif () ++ ++# If we are building Markdown documentation, we have to run some setup after we ++# recurse into methods/. If not, this function is empty. ++post_markdown_setup() +diff --git a/src/mlpack/bindings/CMakeLists.txt b/src/mlpack/bindings/CMakeLists.txt +index f35249ddd..e726c515b 100644 +--- a/src/mlpack/bindings/CMakeLists.txt ++++ b/src/mlpack/bindings/CMakeLists.txt +@@ -1,6 +1,7 @@ + # All we have to do is recurse into the subdirectories. + set(DIRS + cli ++ markdown + python + tests + ) +@@ -9,6 +10,7 @@ foreach(dir ${DIRS}) + add_subdirectory(${dir}) + endforeach() + ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) + set(MLPACK_SRCS ${MLPACK_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) + set(DISABLE_CFLAGS ${DISABLE_CFLAGS} PARENT_SCOPE) +diff --git a/src/mlpack/bindings/cli/CMakeLists.txt b/src/mlpack/bindings/cli/CMakeLists.txt +index 83c50292e..280043fa6 100644 +--- a/src/mlpack/bindings/cli/CMakeLists.txt ++++ b/src/mlpack/bindings/cli/CMakeLists.txt +@@ -25,6 +25,8 @@ set(SOURCES + print_doc_functions_impl.hpp + print_help.hpp + print_help.cpp ++ print_type_doc.hpp ++ print_type_doc_impl.hpp + set_param.hpp + string_type_param.hpp + string_type_param_impl.hpp +diff --git a/src/mlpack/bindings/cli/default_param_impl.hpp b/src/mlpack/bindings/cli/default_param_impl.hpp +index 8c1740c1f..8c9ea7896 100644 +--- a/src/mlpack/bindings/cli/default_param_impl.hpp ++++ b/src/mlpack/bindings/cli/default_param_impl.hpp +@@ -32,7 +32,9 @@ std::string DefaultParamImpl( + std::tuple>>::type* /* junk */) + { + std::ostringstream oss; +- oss << boost::any_cast(data.value); ++ if (!std::is_same::value) ++ oss << boost::any_cast(data.value); ++ + return oss.str(); + } + +@@ -48,9 +50,35 @@ std::string DefaultParamImpl( + std::ostringstream oss; + const T& vector = boost::any_cast(data.value); + oss << "["; +- for (size_t i = 0; i < vector.size() - 1; ++i) +- oss << vector[i] << " "; +- oss << vector[vector.size() - 1] << "]"; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ + return oss.str(); + } + +@@ -67,39 +95,30 @@ std::string DefaultParamImpl( + } + + /** +- * Return the default value of a matrix option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a matrix option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::enable_if_c< + arma::is_arma_type::value || + std::is_same>::value>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ // The filename will always be empty. ++ return "''"; + } + + /** +- * Return the default value of a model option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a model option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::disable_if>::type* /* junk */, + const typename boost::enable_if>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ return "''"; + } + + +diff --git a/src/mlpack/bindings/cli/end_program.hpp b/src/mlpack/bindings/cli/end_program.hpp +index 031c49d5f..8e412db10 100644 +--- a/src/mlpack/bindings/cli/end_program.hpp ++++ b/src/mlpack/bindings/cli/end_program.hpp +@@ -50,7 +50,7 @@ inline void EndProgram() + while (it != parameters.end()) + { + // Now, figure out what type it is, and print it. +- // We can handle strings, ints, bools, floats, doubles. ++ // We can handle strings, ints, bools, doubles. + const util::ParamData& data = it->second; + std::string boostName; + CLI::GetSingleton().functionMap[data.tname]["MapParameterName"](data, +diff --git a/src/mlpack/bindings/cli/get_printable_type.hpp b/src/mlpack/bindings/cli/get_printable_type.hpp +new file mode 100644 +index 000000000..c4634c4ad +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/get_printable_type_impl.hpp b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..bb1ff85a5 +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ if (std::is_same::value) ++ return "flag"; ++ else if (std::is_same::value) ++ return "int"; ++ else if (std::is_same::value) ++ return "double"; ++ else if (std::is_same::value) ++ return "string"; ++ else ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ return "int vector"; ++ else if (std::is_same>::value) ++ return "string vector"; ++ else ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ return "2-d matrix file"; ++ else if (std::is_same>::value) ++ return "2-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else ++ throw std::invalid_argument("unknown Armadillo type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "2-d categorical matrix file"; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return data.cppType + " file"; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_doc_functions.hpp b/src/mlpack/bindings/cli/print_doc_functions.hpp +index cfc024b2c..81ce73816 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions.hpp +@@ -20,12 +20,38 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ ++/** ++ * Print documentation for each of the types. ++ */ ++inline std::string PrintTypeDocs(); ++ + /** + * Given a parameter type, print the corresponding value. + */ + template + inline std::string PrintValue(const T& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -36,6 +62,12 @@ inline std::string PrintDataset(const std::string& dataset); + */ + inline std::string PrintModel(const std::string& model); + ++/** ++ * Print the type of a parameter that a user would specify from the ++ * command-line. ++ */ ++inline std::string PrintType(const util::ParamData& param); ++ + /** + * Base case for recursion. + */ +@@ -56,6 +88,12 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +index 0f37e7d5e..96349f5dd 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +@@ -20,6 +20,31 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ return "mlpack_" + bindingName; ++} ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& /* bindingName */) ++{ ++ return ""; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return ""; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -35,6 +60,23 @@ inline std::string PrintValue(const T& value, bool quotes) + return oss.str(); + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -107,10 +149,76 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args) + { +- return util::HyphenateString("$ " + programName + " " + ++ return util::HyphenateString("$ " + GetBindingName(programName) + " " + + ProcessOptions(args...), 2); + } + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << "$ " << GetBindingName(programName); ++ ++ // Handle all options---first input options, then output options. ++ const std::map& parameters = CLI::Parameters(); ++ ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " "; ++ if (!it->second.required) ++ oss << "["; ++ ++ oss << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ ++ if (!it->second.required) ++ oss << "]"; ++ } ++ ++ // Now get the output options. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " [" << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ oss << "]"; ++ } ++ ++ return util::HyphenateString(oss.str(), 8); ++} ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_help.cpp b/src/mlpack/bindings/cli/print_help.cpp +index c6ef6c90d..ff62a7154 100644 +--- a/src/mlpack/bindings/cli/print_help.cpp ++++ b/src/mlpack/bindings/cli/print_help.cpp +@@ -118,7 +118,11 @@ void PrintHelp(const std::string& param) + } + + // Append default value to description. +- if (pass >= 1 && data.cppType != "bool") ++ if (pass >= 1 && (data.cppType == "int" || data.cppType == "double" || ++ data.cppType == "std::string" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector")) + { + std::string defaultValue; + CLI::GetSingleton().functionMap[data.tname]["DefaultParam"](data, +diff --git a/src/mlpack/bindings/cli/print_type_doc.hpp b/src/mlpack/bindings/cli/print_type_doc.hpp +new file mode 100644 +index 000000000..677854e60 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_type_doc_impl.hpp b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..3a5a20912 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +@@ -0,0 +1,173 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option. If not specified, it is false; if " ++ "specified, it is true."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A vector of integers, separated by commas (i.e., \"1,2,3\")."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A vector of strings, separated by commas (i.e., " ++ "\"hello\",\"goodbye\")."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ return "A data matrix filename. The file can be CSV (.csv), TSV (.csv), " ++ "ASCII (space-separated values, .txt), Armadillo ASCII (.txt), PGM " ++ "(.pgm), PPM (.ppm), Armadillo binary (.bin), or HDF5 (.h5, .hdf, " ++ ".hdf5, or .he5), if mlpack was compiled with HDF5 support. The type " ++ "of the data is detected by the extension of the filename. The storage" ++ " should be such that one row corresponds to one point, and one column " ++ "corresponds to one dimension (this is the typical storage format for " ++ "on-disk data). All values of the matrix will be loaded as double-" ++ "precision floating point data."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A data matrix filename, where the matrix holds only non-negative " ++ "integer values. This type is often used for labels or indices. The " ++ "file can be CSV (.csv), TSV (.csv), ASCII (space-separated values, " ++ ".txt), Armadillo ASCII (.txt), PGM (.pgm), PPM (.ppm), Armadillo " ++ "binary (.bin), or HDF5 (.h5, .hdf, .hdf5, or .he5), if mlpack was " ++ "compiled with HDF5 support. The type of the data is detected by the " ++ "extension of the filename. The storage should be such that one row " ++ "corresponds to one point, and one column corresponds to one dimension " ++ "(this is the typical storage format for on-disk data). All values of " ++ "the matrix will be loaded as unsigned integers."; ++ } ++ else if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "A one-dimensional vector filename. This file can take the same " ++ "formats as the data matrix filenames; however, it must either contain " ++ "one row and many columns, or one column and many rows."; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "A one-dimensional vector filename, where the matrix holds only non-" ++ "negative integer values. This type is typically used for labels or " ++ "predictions or other indices. This file can take the same formats as " ++ "the data matrix filenames; however, it must either contain one row and" ++ " many columns, or one column and many rows."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A filename for a data matrix that can contain categorical " ++ "(non-numeric) data. If the file contains only numeric data, then the " ++ "same formats for regular data matrices can be used. If the file " ++ "contains strings or other values that can't be parsed as numbers, then " ++ "the type to be loaded must be CSV (.csv) or ARFF (.arff). Any non-" ++ "numeric data will be converted to an unsigned integer value, and " ++ "dimensions where the data is converted will be treated as categorical " ++ "dimensions. When using this format, there is no need for one-hot " ++ "encoding of categorical data."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "A filename containing an mlpack model. These can have one of three " ++ "formats: binary (.bin), text (.txt), and XML (.xml). The XML format " ++ "produces the largest (but most human-readable) files, while the binary " ++ "format can be significantly more compact and quicker to load and save."; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/string_type_param.hpp b/src/mlpack/bindings/cli/string_type_param.hpp +index 28caa8991..702fb4668 100644 +--- a/src/mlpack/bindings/cli/string_type_param.hpp ++++ b/src/mlpack/bindings/cli/string_type_param.hpp +@@ -74,12 +74,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + const void* /* input */, + void* output); + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output); +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/cli/string_type_param_impl.hpp b/src/mlpack/bindings/cli/string_type_param_impl.hpp +index 01065086e..86fb96302 100644 +--- a/src/mlpack/bindings/cli/string_type_param_impl.hpp ++++ b/src/mlpack/bindings/cli/string_type_param_impl.hpp +@@ -80,16 +80,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + *outstr = "string"; + } + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output) +-{ +- std::string* outstr = (std::string*) output; +- *outstr = "float"; +-} +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/markdown/CMakeLists.txt b/src/mlpack/bindings/markdown/CMakeLists.txt +new file mode 100644 +index 000000000..b6b31cfba +--- /dev/null ++++ b/src/mlpack/bindings/markdown/CMakeLists.txt +@@ -0,0 +1,209 @@ ++macro (not_found_return message) ++ message(STATUS "${message}") ++ ++ macro (add_markdown_docs name languages category) ++ # Do nothing. ++ endmacro() ++ ++ function (post_markdown_setup) ++ # Do nothing. ++ endfunction () ++ ++ return () ++endmacro () ++ ++if (NOT BUILD_MARKDOWN_BINDINGS) ++ not_found_return("Not building Markdown bindings.") ++endif () ++ ++# We don't need to find any libraries or anything to generate markdown ++# documentation. ++ ++# Get categories. The list of allowable categories for add_markdown_docs() is ++# in that file. ++include(MarkdownCategories.cmake) ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) ++ ++# Add sources for Markdown bindings. ++set(SOURCES ++ "binding_info.hpp" ++ "binding_info.cpp" ++ "default_param.hpp" ++ "get_binding_name.hpp" ++ "get_binding_name.cpp" ++ "get_param.hpp" ++ "get_printable_param.hpp" ++ "get_printable_param_name.hpp" ++ "get_printable_param_name_impl.hpp" ++ "get_printable_type.hpp" ++ "md_option.hpp" ++ "print_doc_functions.hpp" ++ "print_doc_functions_impl.hpp" ++ "print_docs.hpp" ++ "print_docs.cpp" ++ "print_type_doc.hpp" ++ "program_doc_wrapper.hpp" ++) ++ ++# Copy all Markdown sources to the build directory. ++add_custom_target(markdown_copy) ++add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++foreach(file ${SOURCES}) ++ add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} ARGS -E copy ++ ${CMAKE_CURRENT_SOURCE_DIR}/${file} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++endforeach() ++ ++# Create the add_markdown_docs() macro. It's meant to be used as ++# 'add_markdown_docs(knn, "cli;python;julia", "classification")', for instance. ++# See the file 'MarkdownCategories.cmake' for valid categories that can be used ++# by the macro. ++macro (add_markdown_docs name languages category) ++ ++ # First, make sure that the category is a valid category. ++ list(FIND MARKDOWN_CATEGORIES ${category} cat_index) ++ if (${cat_index} EQUAL -1) ++ string(CONCAT error_str "add_markdown_docs(): unknown category ${category}!" ++ " See the categories in " ++ "src/mlpack/bindings/markdown/MarkdownCategories.cmake.") ++ message(FATAL_ERROR "${ERROR_STR}") ++ endif () ++ ++ # Next, we should use configure_file() to generate each ++ # generate_markdown..cpp. We need to loop over all the languages for ++ # this binding to do that. ++ set(BINDING ${name}) ++ set(LANGUAGES_PUSH_BACK_CODE "") ++ set(PROGRAM_MAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${name}_main.cpp") ++ foreach (lang ${languages}) ++ set(LANGUAGES_PUSH_BACK_CODE ++ "${LANGUAGES_PUSH_BACK_CODE}\n languages.push_back(\"${lang}\");") ++ set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} ${lang}) ++ endforeach () ++ list(REMOVE_DUPLICATES MARKDOWN_ALL_LANGUAGES_LIST) ++ ++ # Do the actual file configuration. ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.hpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.hpp) ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.cpp) ++ ++ # Lastly, that generate_markdown..cpp should be added to the list of ++ # files to be compiled for the 'generate_markdown' target. We also need to ++ # add information about this binding to a set of variables we have to track. ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.hpp ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.cpp) ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} ${name}) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} ${category}) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++ set (MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) ++endmacro () ++ ++# After all the methods/ directories have been traversed, we can add the ++# 'generate_markdown' target. This function is run at the bottom of ++# methods/CMakeLists.txt. ++function (post_markdown_setup) ++ # We need to generate the program file. This consists of generating three ++ # things: ++ # ++ # - MARKDOWN_INCLUDES: the list of files to be included. ++ # - MARKDOWN_HEADER_CODE: the code used to print the header/sidebar. ++ # - MARKDOWN_CALL_CODE: the code to actually call the functions to print ++ # documentation for each binding. ++ ++ # Iterate over categories of binding. ++ list(LENGTH MARKDOWN_NAMES NUM_MARKDOWN_BINDINGS) ++ list(LENGTH MARKDOWN_CATEGORIES NUM_MARKDOWN_CATEGORIES) ++ math(EXPR cat_limit "${NUM_MARKDOWN_CATEGORIES} - 1") ++ foreach (i RANGE ${cat_limit}) ++ list (GET MARKDOWN_CATEGORIES ${i} cat) ++ # Put the things in this category in a div. ++ string(CONCAT header_code "${MARKDOWN_HEADER_CODE} cout << " ++ "\"
\" << endl;\n " ++ "cout << \"
${cat}:
\" << endl;\n") ++ set(MARKDOWN_HEADER_CODE ${header_code}) ++ ++ # Add an option for this binding. ++ math(EXPR range_limit "${NUM_MARKDOWN_BINDINGS} - 1") ++ foreach (j RANGE ${range_limit}) ++ list (GET MARKDOWN_NAME_CATEGORIES ${j} category) ++ if (NOT category STREQUAL cat) ++ continue () ++ endif () ++ ++ list (GET MARKDOWN_NAMES ${j} name) ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n Print${name}Headers();") ++ endforeach () ++ ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n cout << \"
\" << endl << endl;") ++ endforeach () ++ ++ foreach (name ${MARKDOWN_NAMES}) ++ set (MARKDOWN_INCLUDE_CODE ++ "${MARKDOWN_INCLUDE_CODE}\n#include \"generate_markdown.${name}.hpp\"") ++ set (MARKDOWN_CALL_CODE "${MARKDOWN_CALL_CODE}\n Print${name}Docs();") ++ endforeach () ++ ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.cpp) ++ ++ # Remember that this is being run from some other directory, so we have to be ++ # explicit with the locations of the files we are compiling against. ++ add_executable(generate_markdown ++ "${BINDING_BINARY_DIR}/generate_markdown.cpp" ++ ${MARKDOWN_SRCS} ++ "${BINDING_SOURCE_DIR}/binding_info.hpp" ++ "${BINDING_SOURCE_DIR}/binding_info.cpp" ++ "${BINDING_SOURCE_DIR}/default_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.cpp" ++ "${BINDING_SOURCE_DIR}/get_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name_impl.hpp" ++ "${BINDING_SOURCE_DIR}/md_option.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions_impl.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.cpp" ++ "${BINDING_SOURCE_DIR}/program_doc_wrapper.hpp" ++ "${BINDING_SOURCE_DIR}/generate_markdown.cpp") ++ target_link_libraries(generate_markdown mlpack ${MLPACK_LIBRARIES}) ++ add_dependencies(generate_markdown markdown_copy) ++ set_target_properties(generate_markdown PROPERTIES ++ COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_MARKDOWN ++ RUNTIME_OUTPUT_DIRECTORY ${BINDING_BINARY_DIR}) ++ ++ add_custom_target(markdown ALL ++ ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/doc/ ++ COMMAND ${CMAKE_COMMAND} ++ -DPROGRAM=${BINDING_BINARY_DIR}/generate_markdown ++ -DOUTPUT_FILE=${CMAKE_BINARY_DIR}/doc/mlpack.md ++ -P ${CMAKE_SOURCE_DIR}/CMake/RunProgram.cmake ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/change_language.js ++ ${CMAKE_BINARY_DIR}/doc/res/change_languages.js ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/menu_bg.png ++ ${CMAKE_BINARY_DIR}/doc/res/menu_bg.png ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/formatting.css ++ ${CMAKE_BINARY_DIR}/doc/res/formatting.css ++ DEPENDS generate_markdown ++ COMMENT "Generating Markdown documentation for mlpack bindings...") ++endfunction () +diff --git a/src/mlpack/bindings/markdown/MarkdownCategories.cmake b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +new file mode 100644 +index 000000000..6b8d697cf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +@@ -0,0 +1,11 @@ ++# This is a list of categories of binding that Markdown can handle. If the ++# category you choose for a Markdown binding is not in this list, an error will ++# be thrown. ++set (MARKDOWN_CATEGORIES ++ "classification" ++ "regression" ++ "clustering" ++ "geometry" ++ "preprocessing" ++ "misc. / other" ++ "transformations") +diff --git a/src/mlpack/bindings/markdown/binding_info.cpp b/src/mlpack/bindings/markdown/binding_info.cpp +new file mode 100644 +index 000000000..0a61103d3 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.cpp +@@ -0,0 +1,49 @@ ++/** ++ * @file binding_info.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of BindingInfo functions. ++ */ ++#include "binding_info.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++util::ProgramDoc& BindingInfo::GetProgramDoc(const std::string& bindingName) ++{ ++ if (GetSingleton().map.count(bindingName) == 0) ++ { ++ throw std::invalid_argument("Binding name '" + bindingName + ++ "' not known!"); ++ } ++ ++ return GetSingleton().map.at(bindingName); ++} ++ ++/** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++void BindingInfo::RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc) ++{ ++ GetSingleton().map[bindingName] = programDoc; ++} ++ ++//! Get or modify the current language (don't set it to something invalid!). ++std::string& BindingInfo::Language() ++{ ++ return GetSingleton().language; ++} ++ ++BindingInfo& BindingInfo::GetSingleton() ++{ ++ static BindingInfo instance; ++ return instance; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/binding_info.hpp b/src/mlpack/bindings/markdown/binding_info.hpp +new file mode 100644 +index 000000000..6a99d2ebc +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file binding_name.hpp ++ * @author Ryan Curtin ++ * ++ * This file defines the BindingInfo singleton class that is used specifically ++ * for the Markdown bindings to map from a binding name (i.e. "knn") to ++ * multiple ProgramDoc objects, which are then used to generate the ++ * documentation. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The BindingInfo class is used by the Markdown documentation generator to ++ * store multiple ProgramDoc objects, indexed by both the binding name (i.e. ++ * "knn") and the language (i.e. "cli"). ++ */ ++class BindingInfo ++{ ++ public: ++ /** ++ * Return a ProgramDoc object for a given bindingName. ++ */ ++ static util::ProgramDoc& GetProgramDoc(const std::string& bindingName); ++ ++ /** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++ static void RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc); ++ ++ //! Get or modify the current language (don't set it to something invalid!). ++ static std::string& Language(); ++ ++ private: ++ //! Private constructor, so that only one instance can be created. ++ BindingInfo() { } ++ ++ //! Get the singleton. ++ static BindingInfo& GetSingleton(); ++ ++ //! Internally-held map for mapping a binding name to a ProgramDoc name. ++ std::unordered_map map; ++ ++ //! Holds the name of the language that we are currently printing. This is ++ //! modified before printing the documentation, and then used by ++ //! print_doc_functions.hpp during printing to print the correct language. ++ std::string language; ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/default_param.hpp b/src/mlpack/bindings/markdown/default_param.hpp +new file mode 100644 +index 000000000..78af807e0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/default_param.hpp +@@ -0,0 +1,45 @@ ++/** ++ * @file default_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get the default value of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the default value of a parameter into the output string. The type ++ * printed depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::DefaultParamImpl::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::DefaultParamImpl::type>(data); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +new file mode 100644 +index 000000000..18a07e5ec +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +@@ -0,0 +1,46 @@ ++/** ++ * @file generate_markdown.binding.cpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#define BINDING_NAME "${BINDING}" ++ ++#include ++#include "generate_markdown.${BINDING}.hpp" ++#include "binding_info.hpp" ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++static const std::string testName = "${BINDING}"; ++#include <${PROGRAM_MAIN_FILE}> ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintHeaders("${BINDING}", languages); ++} ++ ++void Print${BINDING}Docs() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintDocs("${BINDING}", languages); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +new file mode 100644 +index 000000000..3e81c9c3a +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +@@ -0,0 +1,28 @@ ++/** ++ * @file generate_markdown.binding.hpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++ ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers(); ++void Print${BINDING}Docs(); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +new file mode 100644 +index 000000000..abacae377 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +@@ -0,0 +1,114 @@ ++/** ++ * @file generate_markdown.cpp.in ++ * @author Ryan Curtin ++ * ++ * This file is configured by CMake to generate all of the Markdown required by ++ * the project. ++ */ ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++${MARKDOWN_INCLUDE_CODE} ++ ++using namespace mlpack; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++using namespace std; ++ ++int main() ++{ ++ // These come to use from CMake separated by semicolons. ++ string languageList = "${MARKDOWN_ALL_LANGUAGES_LIST}"; ++ vector languages; ++ size_t index; ++ while ((index = languageList.find(';')) != string::npos) ++ { ++ languages.push_back(languageList.substr(0, index)); ++ languageList = languageList.substr(index + 1); ++ } ++ languages.push_back(languageList); ++ ++ cout << "
" << endl; ++ ++ // We need to create the input form selector for the language. ++ cout << "
" << endl; ++ cout << "
" << endl; ++ cout << " " << endl; ++ cout << "
" << endl; ++ cout << "
" << endl; ++ cout << endl; ++ ++ // The links to the data type sections get put here. ++ cout << " - [mlpack overview](#mlpack-overview){: .language-link #always }" ++ << endl; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << " - [data formats](#" << languages[i] << "_data-formats){: " ++ << ".language-link #" << languages[i] << " }" << endl; ++ } ++ ++ ${MARKDOWN_HEADER_CODE} ++ ++ cout << endl << "
" << endl << endl; ++ ++ cout << "
" << endl; ++ cout << endl; ++ ++ // Create all the headers for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "
" << endl; ++ cout << "# " << util::GetVersion() << " " << PrintLanguage(languages[i]) ++ << " binding documentation" << endl; ++ cout << "
" << endl; ++ } ++ ++ /** ++ * "mlpack overview" section. This will go at the top of the page. ++ */ ++ cout << "## mlpack overview" << endl; ++ cout << endl; ++ cout << "mlpack is an intuitive, fast, and flexible C++ machine learning " ++ "library with bindings to other languages. It is meant to be a machine " ++ "learning analog to LAPACK, and aims to implement a wide array of machine" ++ " learning methods and functions as a \"swiss army knife\" for machine " ++ "learning researchers."; ++ cout << endl << endl; ++ cout << "This reference page details mlpack's bindings to other languages. " ++ "Further useful mlpack documentation links are given below."; ++ cout << endl << endl; ++ cout << " - [mlpack homepage](https://www.mlpack.org/)" << endl; ++ cout << " - [mlpack on Github](https://github.com/mlpack/mlpack)" << endl; ++ cout << " - [mlpack main documentation page]" ++ << "(https://www.mlpack.org/docs.html)" << endl; ++ cout << endl; ++ ++ /** ++ * Discussion of different data types section. This goes just below the ++ * overview section at the top of the page. ++ */ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ cout << PrintTypeDocs() << endl; ++ } ++ ++ ${MARKDOWN_CALL_CODE} ++ cout << "
" << endl << endl; ++ ++ // Make sure script gets included for changeLanguage(). ++ cout << "" << endl; ++} +diff --git a/src/mlpack/bindings/markdown/get_binding_name.cpp b/src/mlpack/bindings/markdown/get_binding_name.cpp +new file mode 100644 +index 000000000..67d229b6c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.cpp +@@ -0,0 +1,40 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#include "get_binding_name.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++std::string GetBindingName(const std::string& language, ++ const std::string& name) ++{ ++ // Unfortunately, every time a new binding is added, this code will need to be ++ // modified. ++ if (language == "cli") ++ { ++ // For command-line programs, all bindings have 'mlpack_' prepended to the ++ // name. ++ return "mlpack_" + name; ++ } ++ else if (language == "python") ++ { ++ // For Python bindings, the name is unchanged. ++ return name; ++ } ++ else ++ { ++ throw std::invalid_argument("Don't know how to compute binding name for " ++ "language \"" + language + "\"! Is the language specified in " ++ "src/mlpack/bindings/markdown/get_binding_name.cpp?"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/get_binding_name.hpp b/src/mlpack/bindings/markdown/get_binding_name.hpp +new file mode 100644 +index 000000000..21c55f05c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.hpp +@@ -0,0 +1,30 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given a language name and a binding name, return the name of that binding for ++ * that language. Note that if a new language is added to the mlpack bindings, ++ * this method will need to be updated so that documentation can be successfully ++ * generated for that language. ++ */ ++std::string GetBindingName(const std::string& language, ++ const std::string& name); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_param.hpp b/src/mlpack/bindings/markdown/get_param.hpp +new file mode 100644 +index 000000000..6c1611f49 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_param.hpp +@@ -0,0 +1,38 @@ ++/** ++ * @file get_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a parameter for a Markdown binding. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * All Markdown binding types are exactly what is held in the ParamData, so no ++ * special handling is necessary. ++ */ ++template ++void GetParam(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ util::ParamData& dmod = const_cast(d); ++ *((T**) output) = boost::any_cast(&dmod.value); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param.hpp b/src/mlpack/bindings/markdown/get_printable_param.hpp +new file mode 100644 +index 000000000..53a862b61 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param.hpp +@@ -0,0 +1,126 @@ ++/** ++ * @file get_printable_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a printable version of parameters. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print an option of a simple type. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a vector option, with spaces between it. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ const T& t = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ for (size_t i = 0; i < t.size(); ++i) ++ oss << t[i] << " "; ++ return oss.str(); ++} ++ ++/** ++ * Print a matrix option (this prints its size). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ // Get the matrix. ++ const T& matrix = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix"; ++ return oss.str(); ++} ++ ++/** ++ * Print a serializable class option (this prints the class name). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << data.cppType << " model at " << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a combination DatasetInfo/matrix parameter. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0) ++{ ++ // Get the matrix. ++ const T& tuple = boost::any_cast(data.value); ++ const arma::mat& matrix = std::get<1>(tuple); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix with dimension type " ++ << "information"; ++ return oss.str(); ++} ++ ++/** ++ * Print an option into a std::string. This should print a short, one-line ++ * representation of the object. The string will be stored in the output ++ * pointer. ++ * ++ * @param data Parameter data struct. ++ * @param input Unused parameter. ++ * @param output Output storage for the string. ++ */ ++template ++void GetPrintableParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParam::type>(data); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name.hpp b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +new file mode 100644 +index 000000000..9ceca6b4c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +@@ -0,0 +1,83 @@ ++/** ++ * @file get_printable_param_name.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamName( ++ const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamName::type>(d); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_name_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +new file mode 100644 +index 000000000..41b9c2972 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_param_name_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "--" + data.name; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value.hpp b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +new file mode 100644 +index 000000000..d58bc93bf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +@@ -0,0 +1,88 @@ ++/** ++ * @file get_printable_param_value.hpp ++ * @author Ryan Curtin ++ * ++ * Given a parameter value, print what the user might actually specify on the ++ * command line. Basically this adds ".csv" to types where data must be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamValue( ++ const util::ParamData& d, ++ const void* input, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamValue::type>(d, ++ *((std::string*) input)); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_value_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +new file mode 100644 +index 000000000..9fce0559b +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +@@ -0,0 +1,84 @@ ++/** ++ * @file get_printable_param_value_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter value that the user would specify on the command line ++ * depending on the type of the option. Basically this adds ".csv" to types ++ * that need to be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return input; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".csv"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".bin"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>>::type*) ++{ ++ return input + ".arff"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_type.hpp b/src/mlpack/bindings/markdown/get_printable_type.hpp +new file mode 100644 +index 000000000..2b292b937 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_type.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::GetPrintableType::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::GetPrintableType::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("GetPrintableType(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the type of a parameter. The type printed depends on the current ++ * setting of BindingInfo::Language(). ++ */ ++template ++std::string GetPrintableType(const util::ParamData& data) ++{ ++ std::string output; ++ GetPrintableType(data, (void*) NULL, (void*) &output); ++ return output; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/is_serializable.hpp b/src/mlpack/bindings/markdown/is_serializable.hpp +new file mode 100644 +index 000000000..b129487b9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/is_serializable.hpp +@@ -0,0 +1,63 @@ ++/** ++ * @file is_serializable.hpp ++ * @author Ryan Curtin ++ * ++ * Return a bool noting whether or not a parameter is serializable. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Return false, because the type is not serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::disable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return false, because even though the type is serializable, it is an ++ * Armadillo type not an mlpack model. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return true, because the type is serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0) ++{ ++ return true; ++} ++ ++/** ++ * Return whether or not the type is serializable. ++ */ ++template ++void IsSerializable(const util::ParamData& /* data */, ++ const void* /* input */, ++ void* output) ++{ ++ *((bool*) output) = IsSerializable::type>(); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/md_option.hpp b/src/mlpack/bindings/markdown/md_option.hpp +new file mode 100644 +index 000000000..dc728c4e9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/md_option.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file md_option.hpp ++ * @author Ryan Curtin ++ * ++ * The Markdown option type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++#define MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++ ++#include ++#include ++#include "default_param.hpp" ++#include "get_param.hpp" ++#include "get_printable_param.hpp" ++#include "get_printable_param_name.hpp" // For cli bindings. ++#include "get_printable_param_value.hpp" // For cli bindings. ++#include "get_printable_type.hpp" ++#include "is_serializable.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The Markdown option class. ++ */ ++template ++class MDOption ++{ ++ public: ++ /** ++ * Construct an MDOption object. When constructed, it will register itself ++ * with CLI. The testName parameter is not used and added for compatibility ++ * reasons. ++ */ ++ MDOption(const T defaultValue, ++ const std::string& identifier, ++ const std::string& description, ++ const std::string& alias, ++ const std::string& cppName, ++ const bool required = false, ++ const bool input = true, ++ const bool noTranspose = false, ++ const std::string& bindingName = "") ++ { ++ // Create the ParamData object to give to CLI. ++ util::ParamData data; ++ ++ data.desc = description; ++ data.name = identifier; ++ data.tname = TYPENAME(T); ++ data.alias = alias[0]; ++ data.wasPassed = false; ++ data.noTranspose = noTranspose; ++ data.required = required; ++ data.input = input; ++ data.loaded = false; ++ // Several options from Python and CLI bindings are persistent. ++ if (identifier == "verbose" || identifier == "copy_all_inputs" || ++ identifier == "help" || identifier == "info" || identifier == "version") ++ data.persistent = true; ++ else ++ data.persistent = false; ++ data.cppType = cppName; ++ ++ // Every parameter we'll get from Markdown will have the correct type. ++ data.value = boost::any(defaultValue); ++ ++ // Restore the parameters for this program. ++ if (identifier != "verbose" && identifier != "copy_all_inputs") ++ CLI::RestoreSettings(bindingName, false); ++ ++ // Set the function pointers that we'll need. Most of these simply delegate ++ // to the current binding type's implementation. Any new language will need ++ // to have all of these implemented, and the Markdown implementation will ++ // need to properly delegate. ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetParam"] = &GetParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = ++ &GetPrintableParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamName"] = ++ &GetPrintableParamName; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamValue"] = ++ &GetPrintableParamValue; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableType"] = ++ &GetPrintableType; ++ CLI::GetSingleton().functionMap[data.tname]["IsSerializable"] = ++ &IsSerializable; ++ ++ // Add the option. ++ CLI::Add(std::move(data)); ++ if (identifier != "verbose" && identifier != "copy_all_inputs" && ++ identifier != "help" && identifier != "info" && identifier != "version") ++ CLI::StoreSettings(bindingName); ++ CLI::ClearSettings(); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions.hpp b/src/mlpack/bindings/markdown/print_doc_functions.hpp +new file mode 100644 +index 000000000..2430fa512 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file print_doc_functions.hpp ++ * @author Ryan Curtin ++ * ++ * This file wraps the different printing functionality of different binding ++ * types. If a new binding type is added, this code will need to be modified so ++ * that Markdown can be printed. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language); ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(const std::string& language); ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs(); ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes); ++ ++/** ++ * Print the default value of an option, unless it is required (in which case ++ * Markdown italicized '--' is printed). ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset); ++ ++/** ++ * Print a model type parameter (add .bin and return). ++ */ ++inline std::string PrintModel(const std::string& model); ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args); ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName); ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d); ++ ++/** ++ * Return whether or not a runtime check on parameters should be ignored. ++ */ ++template ++inline bool IgnoreCheck(const T& t); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "print_doc_functions_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +new file mode 100644 +index 000000000..13359441d +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +@@ -0,0 +1,540 @@ ++/** ++ * @file print_doc_functions_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Call out to different printing functionality for different binding languages. ++ * If a new binding is added, this code must be modified. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++ ++#include "print_doc_functions.hpp" ++#include "binding_info.hpp" ++#include "print_type_doc.hpp" ++#include "get_printable_type.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::GetBindingName(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::GetBindingName(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language) ++{ ++ if (language == "cli") ++ { ++ return "CLI"; ++ } ++ else if (language == "python") ++ { ++ return "Python"; ++ } ++ else ++ { ++ throw std::invalid_argument("PrintLanguage(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintImport(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintImport(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintImport(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintOutputOptionInfo(); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintOutputOptionInfo(); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintOutputOptionInfo(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++namespace priv { ++ ++// We'll need a fake class for printing model type documentation. ++class mlpackModel ++{ ++ public: ++ // Fake serialization to make SFINAE work right for this type. ++ template ++ void serialize(Archive&, const unsigned int) {} ++}; ++ ++} // namespace priv ++ ++// Utility function that returns the first word (as delimited by spaces) of a ++// string. ++inline std::string ToUnderscores(const std::string& str) ++{ ++ std::string ret(str); ++ std::replace(ret.begin(), ret.end(), ' ', '_'); ++ return ret; ++} ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs() ++{ ++ std::ostringstream oss; ++ oss << "
" << std::endl; ++ oss << "## data formats" << std::endl; ++ oss << "{: .language-types-h2 #" << BindingInfo::Language() ++ << "_data-formats }" << std::endl; ++ oss << std::endl; ++ ++ // Iterate through each of the types that we care about. ++ oss << "mlpack bindings for " << PrintLanguage(BindingInfo::Language()) ++ << " take and return a restricted set of types, for simplicity. These " ++ << "include primitive types, matrix/vector types, categorical matrix " ++ << "types, and model types. Each type is detailed below." << std::endl; ++ oss << std::endl; ++ ++ // Create fake ParamData to pass around. ++ util::ParamData data; ++ data.desc = "fake"; ++ data.name = "fake"; ++ data.tname = std::string(typeid(int).name()); ++ data.cppType = "int"; ++ data.alias = 'f'; ++ data.wasPassed = false; ++ data.noTranspose = true; ++ data.required = false; ++ data.input = true; ++ data.loaded = false; ++ data.persistent = false; ++ data.value = boost::any(int(0)); ++ ++ std::string type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(double).name()); ++ data.cppType = "double"; ++ data.value = boost::any(double(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(bool).name()); ++ data.cppType = "double"; ++ data.value = boost::any(bool(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(std::string).name()); ++ data.cppType = "std::string"; ++ data.value = boost::any(std::string("")); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: " << "#doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ ++ data.tname = std::string(typeid(arma::mat).name()); ++ data.cppType = "arma::mat"; ++ data.value = boost::any(arma::mat()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Mat).name()); ++ data.cppType = "arma::Mat"; ++ data.value = boost::any(arma::Mat()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::rowvec).name()); ++ data.cppType = "arma::rowvec"; ++ data.value = boost::any(arma::rowvec()); ++ const std::string& rowType = GetPrintableType(data); ++ ++ oss << " - `" << rowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(rowType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Row).name()); ++ data.cppType = "arma::Row"; ++ data.value = boost::any(arma::Row()); ++ const std::string& urowType = GetPrintableType>(data); ++ ++ oss << " - `" << urowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(urowType) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::vec).name()); ++ data.cppType = "arma::vec"; ++ data.value = boost::any(arma::vec()); ++ const std::string& colType = GetPrintableType(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (colType != rowType) ++ { ++ oss << " - `" << colType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(colType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ } ++ ++ data.tname = std::string(typeid(arma::Col).name()); ++ data.cppType = "arma::Col"; ++ data.value = boost::any(arma::Col()); ++ const std::string& ucolType = GetPrintableType>(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (ucolType != urowType) ++ { ++ oss << " - `" << ucolType << "`{ #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(ucolType) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ } ++ ++ data.tname = ++ std::string(typeid(std::tuple).name()); ++ data.cppType = "std::tuple"; ++ data.value = boost::any(std::tuple()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(priv::mlpackModel).name()); ++ data.cppType = "mlpackModel"; ++ data.value = boost::any(new priv::mlpackModel()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() ++ << "_model }: " << PrintTypeDoc(data) << std::endl; ++ ++ // Clean up memory. ++ delete boost::any_cast(data.value); ++ ++ oss << std::endl << "
" << std::endl; ++ ++ return oss.str(); ++} ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintValue(value, quotes); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintValue(value, quotes); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter" + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::ostringstream oss; ++ ++ if (d.required) ++ { ++ oss << "**--**"; ++ } ++ else ++ { ++ if (BindingInfo::Language() == "cli") ++ { ++ oss << cli::PrintDefault(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ oss << python::PrintDefault(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDefault: unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ } ++ ++ return oss.str(); ++} ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintDataset(dataset); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintDataset(dataset); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDataset(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Print a model type parameter. ++ */ ++inline std::string PrintModel(const std::string& model) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintModel(model); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintModel(model); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintModel(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ s += cli::ProgramCall(programName, args...); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ s += python::ProgramCall(programName, args...); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```"; ++ return s; ++} ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += "$ " + import + "\n"; ++ s += cli::ProgramCall(programName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += ">>> " + import + "\n"; ++ s += python::ProgramCall(programName); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```\n"; ++ return s; ++} ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName) ++{ ++ // These functions always put a '' around the parameter, so we will skip that ++ // bit. ++ std::string s; ++ if (BindingInfo::Language() == "cli") ++ { ++ // The CLI bindings put a '' around the parameter, so skip that... ++ s = cli::ParamString(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s = python::ParamString(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("ParamString(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + s.substr(1, s.size() - 2) + "`"; ++} ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d) ++{ ++ std::string output; ++ CLI::GetSingleton().functionMap[d.tname]["GetPrintableType"](d, NULL, ++ &output); ++ // We want to make this a link to the type documentation. ++ std::string anchorType = output; ++ bool result; ++ CLI::GetSingleton().functionMap[d.tname]["IsSerializable"](d, NULL, &result); ++ if (result) ++ anchorType = "model"; ++ ++ return "[`" + output + "`](#doc_" + BindingInfo::Language() + "_" + ++ ToUnderscores(anchorType) + ")"; ++} ++ ++template ++inline bool IgnoreCheck(const T& t) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::IgnoreCheck(t); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::IgnoreCheck(t); ++ } ++ else ++ { ++ throw std::invalid_argument("IgnoreCheck(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_docs.cpp b/src/mlpack/bindings/markdown/print_docs.cpp +new file mode 100644 +index 000000000..2711a8789 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.cpp +@@ -0,0 +1,218 @@ ++/** ++ * @file print_docs.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of functions to print Markdown from documentation. ++ */ ++#include "print_docs.hpp" ++ ++#include ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++ ++// Make sure that this is defined. ++#ifndef DOXYGEN_PREFIX ++#define DOXYGEN_PREFIX "https://mlpack.org/docs/mlpack-git/doxygen/" ++#endif ++ ++using namespace std; ++using namespace mlpack; ++using namespace mlpack::util; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages) ++{ ++ // We just want to print the name of the function and a link, as Markdown. ++ // We have to mark it as having the right language with a div. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << " - [" << GetBindingName(bindingName) << "](#" << languages[i] ++ << "_" << bindingName << "){: .language-link #" << languages[i] << " }" ++ << endl; ++ } ++} ++ ++void PrintDocs(const std::string& bindingName, ++ const vector& languages) ++{ ++ ProgramDoc& programDoc = BindingInfo::GetProgramDoc(bindingName); ++ ++ CLI::RestoreSettings(bindingName); ++ ++ // First, for this section, print each of the names. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
" << endl; ++ cout << "## " << GetBindingName(bindingName) << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName << " }" << endl; ++ cout << "
" << endl; ++ } ++ cout << endl; ++ ++ // Next we want to print the logical name of the binding (that's known by ++ // ProgramInfo). ++ cout << "#### " << programDoc.programName << endl; ++ cout << endl; ++ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
" << endl; ++ cout << ProgramCall(bindingName); ++ cout << "
" << endl; ++ } ++ cout << endl; ++ ++ cout << programDoc.shortDocumentation << " "; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "[Detailed documentation](#" << languages[i] << "_" ++ << bindingName << "_detailed-documentation){: .language-detail-link #" ++ << languages[i] << " }"; ++ } ++ cout << "." << endl; ++ ++ // Next, print the PROGRAM_INFO() documentation for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ // This works with the Kramdown processor. ++ cout << "
" << endl; ++ ++ // We need to print the signature. ++ ++ // Now, iterate through each of the input options. ++ cout << endl; ++ cout << "### Input options" << endl; ++ cout << endl; ++ ++ cout << "| ***name*** | ***type*** | ***description*** | ***default*** |" ++ << endl; ++ cout << "|------------|------------|-------------------|---------------|" ++ << endl; ++ map& parameters = CLI::Parameters(); ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ continue; ++ ++ // There are some special options that don't exist in some languages. ++ if (languages[i] != "python" && it->second.name == "copy_all_inputs") ++ continue; ++ if (languages[i] != "cli" && ++ (it->second.name == "help" || it->second.name == "info" || ++ it->second.name == "version")) ++ continue; ++ ++ // Print name, type, description, default. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; // just a string ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " | "; ++ string def = PrintDefault(it->second.name); ++ if (def.size() > 0) ++ cout << "`" << def << "` |"; ++ else ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ // Next, iterate through the list of output options. ++ cout << "### Output options" << endl; ++ cout << endl; ++ string outputInfo = PrintOutputOptionInfo(); ++ if (outputInfo.size() > 0) ++ cout << outputInfo << endl; ++ cout << endl; ++ cout << "| ***name*** | ***type*** | ***description*** |" << endl; ++ cout << "|------------|------------|-------------------|" << endl; ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print name, type, description. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ cout << "### Detailed documentation" << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName ++ << "_detailed-documentation }" << endl; ++ cout << endl; ++ cout << programDoc.documentation() << endl; ++ cout << endl; ++ ++ cout << "### See also" << endl; ++ cout << endl; ++ for (size_t j = 0; j < programDoc.seeAlso.size(); ++j) ++ { ++ cout << " - " << "["; ++ // We need special processing if the user has specified a binding name ++ // starting with @ (i.e., '@kfn' or similar). ++ if (programDoc.seeAlso[j].first[0] == '@') ++ cout << GetBindingName(programDoc.seeAlso[j].first.substr(1)); ++ else ++ cout << programDoc.seeAlso[j].first; ++ cout << "]("; ++ ++ // We need special handling of Doxygen information. ++ if (programDoc.seeAlso[j].second.substr(0, 8) == "@doxygen") ++ { ++ cout << DOXYGEN_PREFIX << programDoc.seeAlso[j].second.substr(9); ++ } ++ else if (programDoc.seeAlso[j].second[0] == '#') ++ { ++ cout << "#" << languages[i] << "_" ++ << programDoc.seeAlso[j].second.substr(1); ++ } ++ else ++ { ++ cout << programDoc.seeAlso[j].second; ++ } ++ ++ cout << ")" << endl; ++ } ++ cout << endl; ++ ++ cout << "
" << endl; ++ cout << endl; ++ } ++ ++ CLI::ClearSettings(); ++} +diff --git a/src/mlpack/bindings/markdown/print_docs.hpp b/src/mlpack/bindings/markdown/print_docs.hpp +new file mode 100644 +index 000000000..abbc88398 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.hpp +@@ -0,0 +1,33 @@ ++/** ++ * @file print_docs.hpp ++ * @author Ryan Curtin ++ * ++ * Functions to generate Markdown for documentation of bindings. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++ ++#include ++ ++/** ++ * Given the current settings of CLI, print the header (which will be the ++ * navigation tab) for the binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ */ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages); ++ ++/** ++ * Given the current settings of CLI, print Markdown documentation for the ++ * binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ * ++ * @param bindingName The binding name (${BINDING} from CMake). ++ * @param languages The set of languages to print documentation for. ++ */ ++void PrintDocs(const std::string& bindingName, ++ const std::vector& languages); ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_type_doc.hpp b/src/mlpack/bindings/markdown/print_type_doc.hpp +new file mode 100644 +index 000000000..108461ed5 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_type_doc.hpp +@@ -0,0 +1,46 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, depending on the current language (as ++ * set in BindingInfo). ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++std::string PrintTypeDoc(const util::ParamData& data) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintTypeDoc::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintTypeDoc::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintTypeDoc(): unknown " ++ "BindingInfo::Language()" + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/program_doc_wrapper.hpp b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +new file mode 100644 +index 000000000..21da3ffc0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +@@ -0,0 +1,41 @@ ++/** ++ * @file program_doc_wrapper.hpp ++ * @author Ryan Curtin ++ * ++ * A simple wrapper around ProgramDoc that also calls ++ * BindingInfo::RegisterProgramDoc() upon construction. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++ ++#include "binding_info.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++class ProgramDocWrapper ++{ ++ public: ++ /** ++ * Construct a ProgramDoc object and register it with ++ * BindingInfo::RegisterProgramDoc(). ++ */ ++ ProgramDocWrapper(const std::string& bindingName, ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& ++ seeAlso) ++ { ++ util::ProgramDoc pd(programName, shortDocumentation, documentation, ++ seeAlso); ++ BindingInfo::RegisterProgramDoc(bindingName, pd); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/res/change_language.js b/src/mlpack/bindings/markdown/res/change_language.js +new file mode 100644 +index 000000000..4b520205f +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/change_language.js +@@ -0,0 +1,102 @@ ++/** ++ * A utility function to change the language displayed on the page. This ++ * function should be called whenever the language is changed from the ++ * drop-down. ++ */ ++function changeLanguage() ++{ ++ lang = document.getElementById("language-select").value; ++ var links = document.getElementsByClassName("language-link"); ++ for (i = 0; i < links.length; ++i) ++ { ++ // With each of the links, we get the inner , but we need the parent ++ //
  • . ++ if (links[i].id == lang || links[i].id == "always") ++ links[i].parentElement.style.display = "list-item"; ++ else ++ links[i].parentElement.style.display = "none"; ++ } ++ ++ var titles = document.getElementsByClassName("language-title"); ++ for (i = 0; i < titles.length; ++i) ++ { ++ if (titles[i].id == lang) ++ titles[i].style.display = "inline"; ++ else ++ titles[i].style.display = "none"; ++ } ++ ++ var headers = document.getElementsByClassName("language-header"); ++ for (i = 0; i < headers.length; ++i) ++ { ++ if (headers[i].id == lang) ++ headers[i].style.display = "inline"; ++ else ++ headers[i].style.display = "none"; ++ } ++ ++ var decls = document.getElementsByClassName("language-decl"); ++ for (i = 0; i < decls.length; ++i) ++ { ++ if (decls[i].id == lang) ++ decls[i].style.display = "inline"; ++ else ++ decls[i].style.display = "none"; ++ } ++ ++ var types = document.getElementsByClassName("language-types"); ++ for (i = 0; i < types.length; ++i) ++ { ++ if (types[i].id == lang) ++ types[i].style.display = "inline"; ++ else ++ types[i].style.display = "none"; ++ } ++ ++ var details = document.getElementsByClassName("language-detail-link"); ++ for (i = 0; i < details.length; ++i) ++ { ++ if (details[i].id == lang) ++ details[i].style.display = "inline"; ++ else ++ details[i].style.display = "none"; ++ } ++ ++ var sections = document.getElementsByClassName("language-section"); ++ for (i = 0; i < sections.length; ++i) ++ { ++ if (sections[i].id == lang) ++ sections[i].style.display = "inline"; ++ else ++ sections[i].style.display = "none"; ++ } ++} ++ ++document.body.onload = function() ++{ ++ // Do we need to manually set the language because the user came with an ++ // anchor? ++ if (window.location.hash) ++ { ++ // Try to extract the language. ++ firstUnderscore = window.location.hash.indexOf("_"); ++ if (firstUnderscore !== -1) ++ { ++ var lang = window.location.hash.substring(1, firstUnderscore); ++ // Now see if it's in the list of languages. ++ var select = document.getElementById("language-select"); ++ for (i = 0; i < select.length; ++i) ++ { ++ var select_lang = select[i].value; ++ // Is the language a match? ++ if (lang === select_lang) ++ { ++ select.value = select_lang; ++ break; ++ } ++ } ++ } ++ } ++ ++ changeLanguage(); ++} +diff --git a/src/mlpack/bindings/markdown/res/formatting.css b/src/mlpack/bindings/markdown/res/formatting.css +new file mode 100644 +index 000000000..487b026e7 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/formatting.css +@@ -0,0 +1,142 @@ ++body ++{ ++ background: #000000; ++} ++ ++/* Don't display other languages by default. */ ++div.language-title, div.language-header, div.language-decl, ++div.language-detail-link, div.language-types, div.language-section ++{ ++ display: none; ++} ++ ++/* Just display cli documentation. */ ++div.language-title#cli, div.language-header#cli, div.language-decl#cli, ++div.language-detail-link#cli, div.language-types#cli, div.language-section#cli ++{ ++ display: none; ++} ++ ++div#header ++{ ++ padding-top: 10px; ++ width: 200px; ++ background: #000000; ++ border-right: 2px solid #333333; ++ height: 100%; ++ position: fixed; ++ z-index: 1; ++ top: 0; ++ left: 0; ++ overflow-y: scroll; ++} ++ ++div#header .language-select-div select ++{ ++ color: #ffffff; ++ background: transparent; ++ border: none; ++ height: 29px; ++ padding: 5px; ++ width: 190px; ++} ++ ++div#header .language-select-div ++{ ++ color: #ffffff; ++ border: 2px solid #333333; ++ background: url('../res/menu_bg.png') no-repeat 96% 0; ++ height: 34px; ++ width: 180px; ++ overflow: hidden; ++ margin-bottom: 20px; ++ ++ -webkit-border-radius: 5px; ++ -moz-border-radius: 5px; ++ border-radius: 5px; ++} ++ ++div#header ul ++{ ++ padding-top: 5px; ++ margin-bottom: 5px; ++ color: #bb2000; ++ display: table; ++ text-align: left; ++ padding-left: 0px; ++ margin-left: 15px; ++ font-size: 75%; ++} ++ ++div#header ul li ++{ ++ line-height: 1.3em; ++} ++ ++div#header ul li a ++{ ++ color: #eab72c; ++ font-weight: none; ++} ++ ++div#header ul li a:hover ++{ ++ color: #ffffff; ++ font-weight: none; ++} ++ ++div#docs ++{ ++ margin-left: 200px; ++} ++ ++span.special ++{ ++ font-size: 85%; ++ color: #eab72c; ++} ++ ++div.category ++{ ++ text-align: left; ++} ++ ++div.category h5 ++{ ++ text-align: left; ++ margin-top: 0; ++ margin-bottom: 0; ++ color: #ffffff; ++ font-size: 100%; ++ font-weight: normal; ++ padding: 0; ++} ++ ++h2#mlpack-overview, h2.language-types-h2 ++{ ++ padding-bottom: 0.75em; ++} ++ ++div.language-header h1 ++{ ++ color: #ffffff; ++ text-align: center; ++ border-top: none; ++ border-bottom: 1px solid #eab72c; ++} ++ ++div.language-types li code ++{ ++ font-weight: bold; ++ color: #eab72c; ++} ++ ++div.language-types li ++{ ++ padding-bottom: 1em; ++} ++ ++div.language-section table td a code:hover ++{ ++ color: #bb2000; ++} +diff --git a/src/mlpack/bindings/markdown/res/menu_bg.png b/src/mlpack/bindings/markdown/res/menu_bg.png +new file mode 100644 +index 0000000000000000000000000000000000000000..c29580a400267755c0f2a7d1b8f154d329797553 +GIT binary patch +literal 395 +zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I +zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@$z$e7@|Ns9$rm%z} +zkO5&YUc4A6vSP&wAh~+=Y9P6B<3=C}Q4qtiwFt=JD+%%oW?-oBD6-sRv)}%J<6##S +z{uAOr8O9`UcNcz%T?{vY9QG1VUsv|Wj9gsIB1StofUaP$^K@|xk+_^3kmk^E@KBlv +z3nQb?wiDJ01q~ZqKi}J1EyfTU{PA6^wa?xsyTXMZR(+r2Hbc2uaP~}(9I0R241Yd| +zZn0k0^$2K^YKdz^NlIc#s#S7PDv)9@GBC8%H89jQGzc*?wK6caGBVIL05S}G9 ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. This is for regular types. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a matrix option, a tuple option, a ++ * serializable option, or a string option (this returns the default filename, ++ * or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */ = 0); ++ ++/** ++ * Return the default value of a model option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of an option. This is the function that will be ++ * placed into the CLI functionMap. ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ std::string* outstr = (std::string*) output; ++ *outstr = DefaultParamImpl::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "default_param_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/default_param_impl.hpp b/src/mlpack/bindings/python/default_param_impl.hpp +new file mode 100644 +index 000000000..bae57cce2 +--- /dev/null ++++ b/src/mlpack/bindings/python/default_param_impl.hpp +@@ -0,0 +1,147 @@ ++/** ++ * @file default_param_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the default value of a parameter, depending on its type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++ ++#include "default_param.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type* /* junk */) ++{ ++ std::ostringstream oss; ++ if (std::is_same::value) ++ oss << "False"; ++ else ++ oss << boost::any_cast(data.value); ++ ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ // Print each element in an array delimited by square brackets. ++ std::ostringstream oss; ++ const T& vector = boost::any_cast(data.value); ++ oss << "["; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ const std::string& s = *boost::any_cast(&data.value); ++ return "'" + s + "'"; ++} ++ ++/** ++ * Return the default value of a matrix option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */) ++{ ++ // Get the filename and return it, or return an empty string. ++ if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "np.empty([0])"; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "np.empty([0], dtype=np.uint64)"; ++ } ++ else if (std::is_same>::value) ++ { ++ return "np.empty([0, 0], dtype=np.uint64)"; ++ } ++ else ++ { ++ return "np.empty([0, 0])"; ++ } ++} ++ ++/** ++ * Return the default value of a model option (always "None"). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ return "None"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_cython_type.hpp b/src/mlpack/bindings/python/get_cython_type.hpp +index e44f4f70b..e44b91eb0 100644 +--- a/src/mlpack/bindings/python/get_cython_type.hpp ++++ b/src/mlpack/bindings/python/get_cython_type.hpp +@@ -40,16 +40,6 @@ inline std::string GetCythonType( + return "int"; + } + +-template<> +-inline std::string GetCythonType( +- const util::ParamData& /* d */, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*) +-{ +- return "float"; +-} +- + template<> + inline std::string GetCythonType( + const util::ParamData& /* d */, +diff --git a/src/mlpack/bindings/python/get_printable_type.hpp b/src/mlpack/bindings/python/get_printable_type.hpp +new file mode 100644 +index 000000000..51a528b4d +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type.hpp +@@ -0,0 +1,120 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++void GetPrintableType(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(d); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_printable_type_impl.hpp b/src/mlpack/bindings/python/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..2031b3435 +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type_impl.hpp +@@ -0,0 +1,151 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "unknown"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "int"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "double"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "string"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "size_t"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "bool"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "list of " + GetPrintableType(d) + "s"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ std::string type = "matrix"; ++ if (std::is_same::value) ++ { ++ if (T::is_row || T::is_col) ++ type = "vector"; ++ } ++ else if (std::is_same::value) ++ { ++ type = "int matrix"; ++ if (T::is_row || T::is_col) ++ type = "int vector"; ++ } ++ ++ return type; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type*) ++{ ++ return "categorical matrix"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return d.cppType + "Type"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_doc.hpp b/src/mlpack/bindings/python/print_doc.hpp +index 7a5a27781..b8962ccaf 100644 +--- a/src/mlpack/bindings/python/print_doc.hpp ++++ b/src/mlpack/bindings/python/print_doc.hpp +@@ -14,7 +14,7 @@ + + #include + #include +-#include "get_python_type.hpp" ++#include "get_printable_type.hpp" + + namespace mlpack { + namespace bindings { +@@ -44,24 +44,20 @@ void PrintDoc(const util::ParamData& d, + oss << d.name << "_ ("; + else + oss << d.name << " ("; +- oss << GetPythonType::type>(d) << "): " ++ oss << GetPrintableType::type>(d) << "): " + << d.desc; + + // Print a default, if possible. + if (!d.required) + { +- if (d.cppType == "std::string") ++ // Call the correct overload to get the default value directly. ++ if (d.cppType == "std::string" || d.cppType == "double" || ++ d.cppType == "int" || d.cppType == "std::vector" || ++ d.cppType == "std::vector" || ++ d.cppType == "std::vector") + { +- oss << " Default value '" << boost::any_cast(d.value) +- << "'."; +- } +- else if (d.cppType == "double") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; +- } +- else if (d.cppType == "int") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; ++ std::string defaultValue = DefaultParamImpl(d); ++ oss << " Default value " << defaultValue << "."; + } + } + +diff --git a/src/mlpack/bindings/python/print_doc_functions.hpp b/src/mlpack/bindings/python/print_doc_functions.hpp +index a9207c570..51e85b78d 100644 +--- a/src/mlpack/bindings/python/print_doc_functions.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions.hpp +@@ -19,6 +19,21 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -29,6 +44,11 @@ inline std::string PrintValue(const T& value, bool quotes); + template<> + inline std::string PrintValue(const bool& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + // Recursion base case. + inline std::string PrintInputOptions(); + +@@ -57,6 +77,12 @@ std::string PrintOutputOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +diff --git a/src/mlpack/bindings/python/print_doc_functions_impl.hpp b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +index c169f72c8..3ff526011 100644 +--- a/src/mlpack/bindings/python/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +@@ -19,6 +19,32 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ // No modification is needed to the name---we just use it as-is. ++ return bindingName + "()"; ++} ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ return "from mlpack import " + bindingName; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return "Results are returned in a Python dictionary. The keys of the " ++ "dictionary are the names of the output parameters."; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -48,6 +74,23 @@ inline std::string PrintValue(const bool& value, bool quotes) + return "False"; + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + // Recursion base case. + std::string PrintInputOptions() { return ""; } + +@@ -136,7 +179,8 @@ std::string PrintOutputOptions(const std::string& paramName, + + /** + * Given a name of a binding and a variable number of arguments (and their +- * contents), print the corresponding function call. ++ * contents), print the corresponding function call. The given programName ++ * should not be the output of GetBindingName(). + */ + template + std::string ProgramCall(const std::string& programName, Args... args) +@@ -159,6 +203,76 @@ std::string ProgramCall(const std::string& programName, Args... args) + return util::HyphenateString(call, 2) + "\n" + oss.str(); + } + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. The programName should not be the output of GetBindingName(). ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << ">>> "; ++ ++ // Determine if we have any output options. ++ const std::map& parameters = CLI::Parameters(); ++ bool hasOutput = false; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ { ++ hasOutput = true; ++ break; ++ } ++ } ++ ++ if (hasOutput) ++ oss << "d = "; ++ ++ oss << programName << "("; ++ ++ // Now iterate over every input option. ++ bool first = true; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ if (!first) ++ oss << ", "; ++ else ++ first = false; ++ ++ // Print the input option. ++ if (it->second.name != "lambda") // Don't print Python keywords. ++ oss << it->second.name << "="; ++ else ++ oss << it->second.name << "_="; ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ oss << value; ++ } ++ oss << ")"; ++ ++ std::string result = util::HyphenateString(oss.str(), 8); ++ ++ oss.str(""); ++ oss << result; ++ ++ // Now print output lines. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print a new line for the output option. ++ oss << std::endl << ">>> " << it->second.name << " = d['" ++ << it->second.name << "']"; ++ } ++ ++ return oss.str(); ++} ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +@@ -176,14 +290,6 @@ inline std::string PrintDataset(const std::string& datasetName) + return "'" + datasetName + "'"; + } + +-/** +- * Given the name of a binding, print its invocation. +- */ +-inline std::string ProgramCall(const std::string& programName) +-{ +- return ">>> " + programName + "("; +-} +- + /** + * Print any closing call to a program. For a Python binding this is a closing + * brace. +diff --git a/src/mlpack/bindings/python/print_input_processing.hpp b/src/mlpack/bindings/python/print_input_processing.hpp +index e271906cb..04f318a7c 100644 +--- a/src/mlpack/bindings/python/print_input_processing.hpp ++++ b/src/mlpack/bindings/python/print_input_processing.hpp +@@ -17,7 +17,6 @@ + #include "get_numpy_type.hpp" + #include "get_numpy_type_char.hpp" + #include "get_cython_type.hpp" +-#include "get_python_type.hpp" + #include "strip_type.hpp" + + namespace mlpack { +diff --git a/src/mlpack/bindings/python/print_type_doc.hpp b/src/mlpack/bindings/python/print_type_doc.hpp +new file mode 100644 +index 000000000..9a5d6dc71 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_type_doc_impl.hpp b/src/mlpack/bindings/python/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..8d6ae3971 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc_impl.hpp +@@ -0,0 +1,162 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option (True or False)."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A list of integers; i.e., [0, 1, 2]."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A list of strings; i.e., [\"hello\", \"goodbye\"]."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data. This can be a 2-d matrix where " ++ "one dimension has size 1, or it can also be a list, a numpy 1-d " ++ "ndarray, or a 1-d pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data. This can be a list of lists, a " ++ "numpy ndarray, or a pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ } ++ else if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data with a uint64 dtype. This can be" ++ " a 2-d matrix where one dimension has size 1, or it can also be a " ++ "list, a numpy 1-d ndarray, or a 1-d pandas DataFrame. If the dtype " ++ "is not already uint64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data with a uint64 dtype. This can " ++ "be a list of lists, a numpy ndarray, or a pandas DataFrame. If the " ++ "dtype is not already uint64, it will be converted."; ++ } ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A 2-d arraylike containing data. Like the regular 2-d matrices, this" ++ " can be a list of lists, a numpy ndarray, or a pandas DataFrame. " ++ "However, this type can also accept a pandas DataFrame that has columns " ++ "of type 'CategoricalDtype'. These categorical values will be converted " ++ "to numeric indices before being passed to mlpack, and then inside mlpack" ++ " they will be properly treated as categorical variables, so there is no " ++ "need to do one-hot encoding for this matrix type. If the dtype of the " ++ "given matrix is not already float64, it will be converted."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "An mlpack model pointer. This type can be pickled to or from disk, " ++ "and internally holds a pointer to C++ memory containing the mlpack " ++ "model. Note that this means that the mlpack model itself cannot be " ++ "easily inspected in Python; however, the pickled model can be loaded " ++ "in C++ and inspected there."; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/py_option.hpp b/src/mlpack/bindings/python/py_option.hpp +index 98bba2dd5..e175d282b 100644 +--- a/src/mlpack/bindings/python/py_option.hpp ++++ b/src/mlpack/bindings/python/py_option.hpp +@@ -13,6 +13,7 @@ + #define MLPACK_BINDINGS_PYTHON_PY_OPTION_HPP + + #include ++#include "default_param.hpp" + #include "get_param.hpp" + #include "get_printable_param.hpp" + #include "print_class_defn.hpp" +@@ -85,6 +86,9 @@ class PyOption + CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = + &GetPrintableParam; + ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ + // These are used by the pyx generator. + CLI::GetSingleton().functionMap[data.tname]["PrintClassDefn"] = + &PrintClassDefn; +diff --git a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +index cf3c1055d..93daaddc4 100644 +--- a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp ++++ b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +@@ -19,6 +19,7 @@ using namespace mlpack; + using namespace mlpack::kernel; + + PROGRAM_INFO("Python binding test", ++ "A simple program to test Python binding functionality.", + "A simple program to test Python binding functionality. You can build " + "mlpack with the BUILD_TESTS option set to off, and this binding will " + "no longer be built."); +diff --git a/src/mlpack/core/util/cli.cpp b/src/mlpack/core/util/cli.cpp +index f2bf6cec2..1a83080bd 100644 +--- a/src/mlpack/core/util/cli.cpp ++++ b/src/mlpack/core/util/cli.cpp +@@ -27,7 +27,8 @@ using namespace mlpack; + using namespace mlpack::util; + + // Fake ProgramDoc in case none is supplied. +-static ProgramDoc emptyProgramDoc = ProgramDoc("", []() { return ""; }); ++static ProgramDoc emptyProgramDoc = ProgramDoc("", "", []() { return ""; }, ++ {}); + + /* Constructors, Destructors, Copy */ + /* Make the constructor private, to preclude unauthorized instances */ +diff --git a/src/mlpack/core/util/mlpack_main.hpp b/src/mlpack/core/util/mlpack_main.hpp +index 4c915c0c0..8003f38ac 100644 +--- a/src/mlpack/core/util/mlpack_main.hpp ++++ b/src/mlpack/core/util/mlpack_main.hpp +@@ -21,6 +21,7 @@ + #define BINDING_TYPE_CLI 0 + #define BINDING_TYPE_TEST 1 + #define BINDING_TYPE_PYX 2 ++#define BINDING_TYPE_MARKDOWN 128 + #define BINDING_TYPE_UNKNOWN -1 + + #ifndef BINDING_TYPE +@@ -99,9 +100,10 @@ using Option = mlpack::bindings::tests::TestOption; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }) + + #elif(BINDING_TYPE == BINDING_TYPE_PYX) // This is a Python binding. + +@@ -128,9 +130,10 @@ static const std::string testName = ""; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); \ ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }); \ + namespace mlpack { \ + namespace bindings { \ + namespace python { \ +@@ -148,6 +151,59 @@ PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" + + // Nothing else needs to be defined---the binding will use mlpackMain() as-is. + ++#elif BINDING_TYPE == BINDING_TYPE_MARKDOWN ++ ++// It doesn't really matter whether this is true or false... ++#define BINDING_MATRIX_TRANSPOSED true ++ ++// We use BINDING_NAME in PROGRAM_INFO() so it needs to be defined. ++#ifndef BINDING_NAME ++ #error "BINDING_NAME must be defined when BINDING_TYPE is Markdown!" ++#endif ++ ++#include ++#include ++ ++#define PRINT_PARAM_STRING mlpack::bindings::markdown::ParamString ++#define PRINT_PARAM_VALUE mlpack::bindings::markdown::PrintValue ++#define PRINT_DATASET mlpack::bindings::markdown::PrintDataset ++#define PRINT_MODEL mlpack::bindings::markdown::PrintModel ++#define PRINT_CALL mlpack::bindings::markdown::ProgramCall ++#define BINDING_IGNORE_CHECK mlpack::bindings::markdown::IgnoreCheck ++ ++namespace mlpack { ++namespace util { ++ ++template ++using Option = mlpack::bindings::markdown::MDOption; ++ ++} ++} ++ ++#include ++#include ++ ++#undef PROGRAM_INFO ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) static \ ++ mlpack::bindings::markdown::ProgramDocWrapper \ ++ cli_programdoc_dummy_object = \ ++ mlpack::bindings::markdown::ProgramDocWrapper(BINDING_NAME, NAME, \ ++ SHORT_DESC, []() { return DESC; }, { __VA_ARGS__ }); \ ++ ++PARAM_FLAG("verbose", "Display informational messages and the full list of " ++ "parameters and timers at the end of execution.", "v"); ++ ++// CLI-specific parameters. ++PARAM_FLAG("help", "Default help info.", "h"); ++PARAM_STRING_IN("info", "Print help on a specific option.", "", ""); ++PARAM_FLAG("version", "Display the version of mlpack.", "V"); ++ ++// Python-specific parameters. ++PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" ++ " copied before the method is run. This is useful for debugging problems " ++ "where the input parameters are being modified by the algorithm, but can " ++ "slow down the code.", ""); ++ + #else + + #error "Unknown binding type! Be sure BINDING_TYPE is defined if you are " \ +diff --git a/src/mlpack/core/util/param.hpp b/src/mlpack/core/util/param.hpp +index b812887fb..de50f5b00 100644 +--- a/src/mlpack/core/util/param.hpp ++++ b/src/mlpack/core/util/param.hpp +@@ -29,6 +29,21 @@ using DatasetInfo = DatasetMapper; + } // namespace data + } // namespace mlpack + ++/** ++ * Provide a link for a binding's "see also" documentation section, which is ++ * primarily (but not necessarily exclusively) used by the Markdown bindings ++ * This link can be specified by calling SEE_ALSO("description", "link"), where ++ * "description" is the description of the link and "link" may be one of the ++ * following: ++ * ++ * - A direct URL, starting with http:// or https://. ++ * - A page anchor for documentation, referencing another binding by its CMake ++ * binding name, i.e. "#knn". ++ * - A link to a Doxygen page, using the mangled Doxygen name after a ++ * '@doxygen/', i.e., "@doxygen/mlpack1_1_adaboost1_1_AdaBoost". ++ */ ++#define SEE_ALSO(DESCRIPTION, LINK) {DESCRIPTION, LINK} ++ + /** + * Document an executable. Only one instance of this macro should be + * present in your program! Therefore, use it in the main.cpp +@@ -41,14 +56,22 @@ using DatasetInfo = DatasetMapper; + * PARAM_DOUBLE_OUT_REQ(), PARAM_VECTOR_OUT_REQ(), PARAM_STRING_OUT_REQ(). + * + * @param NAME Short string representing the name of the program. ++ * @param SHORT_DESC Short two-sentence description of the program; it should ++ * describe what the program implements and does, and a quick overview of ++ * how it can be used and what it should be used for. + * @param DESC Long string describing what the program does and possibly a + * simple usage example. Newlines should not be used here; this is taken + * care of by CLI (however, you can explicitly specify newlines to denote +- * new paragraphs). ++ * new paragraphs). You can also use printing macros like ++ * PRINT_PARAM_STRING(), PRINT_DATASET(), and others. ++ * @param SEE_ALSOS A set of SEE_ALSO() macros that are used for generating ++ * documentation. See the SEE_ALSO() macro. This is a varargs argument, so ++ * you can add as many SEE_ALSO()s as you like. + */ +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }) ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ } ) + + /** + * Define a flag parameter. +diff --git a/src/mlpack/core/util/program_doc.cpp b/src/mlpack/core/util/program_doc.cpp +index 814573b1c..28d7ee8b8 100644 +--- a/src/mlpack/core/util/program_doc.cpp ++++ b/src/mlpack/core/util/program_doc.cpp +@@ -23,17 +23,33 @@ using namespace std; + * Construct a ProgramDoc object. When constructed, it will register itself + * with CLI. A fatal error will be thrown if more than one is constructed. + * +- * @param programName Short string representing the name of the program. +- * @param documentation Long string containing documentation on how to use the +- * program and what it is. No newline characters are necessary; this is +- * taken care of by CLI later. + * @param defaultModule Name of the default module. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. ++ * @param documentation Long string containing documentation on how to use the ++ * program and what it is. No newline characters are necessary; this is ++ * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ +-ProgramDoc::ProgramDoc(const std::string& programName, +- const std::function& documentation) : ++ProgramDoc::ProgramDoc( ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso) : + programName(programName), +- documentation(documentation) ++ shortDocumentation(shortDocumentation), ++ documentation(documentation), ++ seeAlso(seeAlso) + { + // Register this with CLI. + CLI::RegisterProgramDoc(this); + } ++ ++/** ++ * Construct an empty ProgramDoc object. ++ */ ++ProgramDoc::ProgramDoc() ++{ ++ CLI::RegisterProgramDoc(this); ++} +diff --git a/src/mlpack/core/util/program_doc.hpp b/src/mlpack/core/util/program_doc.hpp +index f5de89fe5..296b15586 100644 +--- a/src/mlpack/core/util/program_doc.hpp ++++ b/src/mlpack/core/util/program_doc.hpp +@@ -33,17 +33,32 @@ class ProgramDoc + * will be returned. + * + * @param programName Short string representing the name of the program. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. + * @param documentation Long string containing documentation on how to use the + * program and what it is. No newline characters are necessary; this is + * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ + ProgramDoc(const std::string& programName, +- const std::function& documentation); ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso); ++ ++ /** ++ * Construct an empty ProgramDoc object. (This is not meant to be used!) ++ */ ++ ProgramDoc(); + + //! The name of the program. + std::string programName; ++ //! The short documentation for the program. ++ std::string shortDocumentation; + //! Documentation for what the program does. + std::function documentation; ++ //! Set of see also information. ++ std::vector> seeAlso; + }; + + } // namespace util +diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt +index 4e6fc3df9..360e79f93 100644 +--- a/src/mlpack/methods/CMakeLists.txt ++++ b/src/mlpack/methods/CMakeLists.txt +@@ -74,3 +74,7 @@ endforeach() + + set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) ++set(MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++set(MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++set(MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) +diff --git a/src/mlpack/methods/adaboost/CMakeLists.txt b/src/mlpack/methods/adaboost/CMakeLists.txt +index 5505f057f..3e85237e0 100644 +--- a/src/mlpack/methods/adaboost/CMakeLists.txt ++++ b/src/mlpack/methods/adaboost/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(adaboost) + add_python_binding(adaboost) ++add_markdown_docs(adaboost "cli;python" "classification") +diff --git a/src/mlpack/methods/adaboost/adaboost_main.cpp b/src/mlpack/methods/adaboost/adaboost_main.cpp +index 2634ff08a..db2656353 100644 +--- a/src/mlpack/methods/adaboost/adaboost_main.cpp ++++ b/src/mlpack/methods/adaboost/adaboost_main.cpp +@@ -46,7 +46,14 @@ using namespace mlpack::decision_stump; + using namespace mlpack::perceptron; + using namespace mlpack::util; + +-PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " ++PROGRAM_INFO("AdaBoost", ++ // Short description. ++ "An implementation of the AdaBoost.MH (Adaptive Boosting) algorithm for " ++ "classification. This can be used to train an AdaBoost model on labeled " ++ "data or use an existing AdaBoost model to predict the classes of new " ++ "points.", ++ // Long description. ++ "This program implements the AdaBoost (or Adaptive " + "Boosting) algorithm. The variant of AdaBoost implemented here is " + "AdaBoost.MH. It uses a weak learner, either decision stumps or " + "perceptrons, and over many iterations, creates a strong learner that is a " +@@ -72,7 +79,7 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + "classes for each point in the test dataset are output to the " + + PRINT_PARAM_STRING("output") + " output parameter. The AdaBoost model " + "itself is output to the " + PRINT_PARAM_STRING("output_model") + +- "output parameter." ++ " output parameter." + "\n\n" + "For example, to run AdaBoost on an input dataset " + + PRINT_DATASET("data") + " with perceptrons as the weak learner type, " +@@ -88,7 +95,15 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + PRINT_DATASET("predictions") + " with the following command: " + "\n\n" + + PRINT_CALL("adaboost", "input_model", "model", "test", "test_data", +- "output", "predictions")); ++ "output", "predictions"), ++ // See also... ++ SEE_ALSO("AdaBoost on Wikipedia", "https://en.wikipedia.org/wiki/AdaBoost"), ++ SEE_ALSO("Improved boosting algorithms using confidence-rated predictions " ++ "(pdf)", "http://rob.schapire.net/papers/SchapireSi98.pdf"), ++ SEE_ALSO("Perceptron", "#perceptron"), ++ SEE_ALSO("Decision Stump", "#decision_stump"), ++ SEE_ALSO("mlpack::adaboost::AdaBoost C++ class documentation", ++ "@doxygen/classmlpack_1_1adaboost_1_1AdaBoost.html")); + + // Input for training. + PARAM_MATRIX_IN("training", "Dataset for training AdaBoost.", "t"); +diff --git a/src/mlpack/methods/approx_kfn/CMakeLists.txt b/src/mlpack/methods/approx_kfn/CMakeLists.txt +index da20af02b..be1a5c4f0 100644 +--- a/src/mlpack/methods/approx_kfn/CMakeLists.txt ++++ b/src/mlpack/methods/approx_kfn/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # This program computes approximate furthest neighbors. + add_cli_executable(approx_kfn) + add_python_binding(approx_kfn) ++add_markdown_docs(approx_kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +index 0928acde0..27a8b9eba 100644 +--- a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp ++++ b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +@@ -22,6 +22,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Approximate furthest neighbor search", ++ // Short description. ++ "An implementation of two strategies for furthest neighbor search. This " ++ "can be used to compute the furthest neighbor of query point(s) from a set " ++ "of points; furthest neighbor models can be saved and reused with future " ++ "query point(s).", ++ // Long description. + "This program implements two strategies for furthest neighbor search. " + "These strategies are:" + "\n\n" +@@ -86,7 +92,18 @@ PROGRAM_INFO("Approximate furthest neighbor search", + PRINT_DATASET("neighbors") + " by calling" + "\n\n" + + PRINT_CALL("approx_kfn", "input_model", "model", "query", "new_query_set", +- "k", 3, "neighbors", "neighbors")); ++ "k", 3, "neighbors", "neighbors"), ++ SEE_ALSO("k-furthest-neighbor search", "#kfn"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Fast approximate furthest neighbors with data-dependent candidate" ++ " selection (pdf)", "http://ratml.org/pub/pdf/2016fast.pdf"), ++ SEE_ALSO("Approximate furthest neighbor in high dimensions (pdf)", ++ "https://pdfs.semanticscholar.org/a4b5/7b9cbf37201fb1d9a56c0f4eefad0466" ++ "9c20.pdf"), ++ SEE_ALSO("mlpack::neighbor::QDAFN class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1QDAFN.html"), ++ SEE_ALSO("mlpack::neighbor::DrusillaSelect class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1DrusillaSelect.html")); + + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); + PARAM_MATRIX_IN("query", "Matrix containing query points.", "q"); +diff --git a/src/mlpack/methods/cf/CMakeLists.txt b/src/mlpack/methods/cf/CMakeLists.txt +index 01796f29d..976304458 100644 +--- a/src/mlpack/methods/cf/CMakeLists.txt ++++ b/src/mlpack/methods/cf/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(cf) + add_python_binding(cf) ++add_markdown_docs(cf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp +index 2676975d1..b47f444df 100644 +--- a/src/mlpack/methods/cf/cf_main.cpp ++++ b/src/mlpack/methods/cf/cf_main.cpp +@@ -26,7 +26,13 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " ++PROGRAM_INFO("Collaborative Filtering", ++ // Short description. ++ "An implementation of several collaborative filtering (CF) techniques for " ++ "recommender systems. This can be used to train a new CF model, or use an" ++ " existing CF model to compute recommendations.", ++ // Long description. ++ "This program performs collaborative " + "filtering (CF) on the given dataset. Given a list of user, item and " + "preferences (the " + PRINT_PARAM_STRING("training") + " parameter), " + "the program will perform a matrix decomposition and then can perform a " +@@ -49,7 +55,7 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "addition, the number of recommendations per user to generate can be " + "specified with the " + PRINT_PARAM_STRING("recommendations") + " " + "parameter, and the number of similar users (the size of the neighborhood) " +- " to be considered when generating recommendations can be specified with " ++ "to be considered when generating recommendations can be specified with " + "the " + PRINT_PARAM_STRING("neighborhood") + " parameter." + "\n\n" + "For performing the matrix decomposition, the following optimization " +@@ -79,7 +85,20 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "call " + "\n\n" + + PRINT_CALL("cf", "input_model", "model", "query", "users", +- "recommendations", 5, "output", "recommendations")); ++ "recommendations", 5, "output", "recommendations"), ++ SEE_ALSO("Collaborative filtering tutorial", "@doxygen/cftutorial.html"), ++ SEE_ALSO("Alternating Matrix Factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Collaborative Filtering on Wikipedia", ++ "https://en.wikipedia.org/wiki/Collaborative_filtering"), ++ SEE_ALSO("Matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Matrix_factorization_" ++ "(recommender_systems)"), ++ SEE_ALSO("Matrix factorization techniques for recommender systems (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.441.3234" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::cf::CFType class documentation", ++ "@doxygen/classmlpack_1_1cf_1_1CFType.html")); + + // Parameters for training a model. + PARAM_MATRIX_IN("training", "Input dataset to perform CF on.", "t"); +diff --git a/src/mlpack/methods/dbscan/CMakeLists.txt b/src/mlpack/methods/dbscan/CMakeLists.txt +index 70939c9d0..47b79d782 100644 +--- a/src/mlpack/methods/dbscan/CMakeLists.txt ++++ b/src/mlpack/methods/dbscan/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(dbscan) + add_python_binding(dbscan) ++add_markdown_docs(dbscan "cli;python" "clustering") +diff --git a/src/mlpack/methods/dbscan/dbscan_main.cpp b/src/mlpack/methods/dbscan/dbscan_main.cpp +index c4afc3c85..04bcfc85f 100644 +--- a/src/mlpack/methods/dbscan/dbscan_main.cpp ++++ b/src/mlpack/methods/dbscan/dbscan_main.cpp +@@ -27,6 +27,10 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("DBSCAN clustering", ++ // Short description. ++ "An implementation of DBSCAN clustering. Given a dataset, this can " ++ "compute and return a clustering of that dataset.", ++ // Long description. + "This program implements the DBSCAN algorithm for clustering using " + "accelerated tree-based range search. The type of tree that is used " + "may be parameterized, or brute-force range search may also be used." +@@ -59,7 +63,13 @@ PROGRAM_INFO("DBSCAN clustering", + PRINT_DATASET("input") + " with a radius of 0.5 and a minimum cluster size" + " of 5 is given below:" + "\n\n" + +- PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5)); ++ PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5), ++ SEE_ALSO("DBSCAN on Wikipedia", "https://en.wikipedia.org/wiki/DBSCAN"), ++ SEE_ALSO("A density-based algorithm for discovering clusters in large " ++ "spatial databases with noise (pdf)", ++ "http://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf"), ++ SEE_ALSO("mlpack::dbscan::DBSCAN class documentation", ++ "@doxygen/classmlpack_1_1dbscan_1_1DBSCAN.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to cluster.", "i"); + PARAM_UROW_OUT("assignments", "Output matrix for assignments of each " +diff --git a/src/mlpack/methods/decision_stump/CMakeLists.txt b/src/mlpack/methods/decision_stump/CMakeLists.txt +index e9d9c04c1..a11b0048e 100644 +--- a/src/mlpack/methods/decision_stump/CMakeLists.txt ++++ b/src/mlpack/methods/decision_stump/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_stump) + add_python_binding(decision_stump) ++add_markdown_docs(decision_stump "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_stump/decision_stump_main.cpp b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +index aa4d9a67d..6f8f308af 100644 +--- a/src/mlpack/methods/decision_stump/decision_stump_main.cpp ++++ b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +@@ -22,6 +22,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Decision Stump", ++ // Short description. ++ "An implementation of a decision stump, which is a single-level decision " ++ "tree. Given labeled data, a new decision stump can be trained; or, an " ++ "existing decision stump can be used to classify points.", ++ // Long description. + "This program implements a decision stump, which is a single-level decision" + " tree. The decision stump will split on one dimension of the input data, " + "and will split into multiple buckets. The dimension and bins are selected" +@@ -61,7 +66,12 @@ PROGRAM_INFO("Decision Stump", + "\n\n" + "After training, a decision stump can be saved with the " + + PRINT_PARAM_STRING("output_model") + " output parameter. That stump may " +- "later be re-used in subsequent calls to this program (or others)."); ++ "later be re-used in subsequent calls to this program (or others).", ++ SEE_ALSO("Decision tree", "#decision_tree"), ++ SEE_ALSO("Decision stumps on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_stump"), ++ SEE_ALSO("mlpack::decision_stump::DecisionStump class documentation", ++ "@doxygen/classmlpack_1_1decision__stump_1_1DecisionStump.html")); + + // Datasets we might load. + PARAM_MATRIX_IN("training", "The dataset to train on.", "t"); +diff --git a/src/mlpack/methods/decision_tree/CMakeLists.txt b/src/mlpack/methods/decision_tree/CMakeLists.txt +index 448e4d62e..3f46f0d95 100644 +--- a/src/mlpack/methods/decision_tree/CMakeLists.txt ++++ b/src/mlpack/methods/decision_tree/CMakeLists.txt +@@ -27,3 +27,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_tree) + add_python_binding(decision_tree) ++add_markdown_docs(decision_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_tree/decision_tree_main.cpp b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +index 258689fb6..99a98df4e 100644 +--- a/src/mlpack/methods/decision_tree/decision_tree_main.cpp ++++ b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +@@ -21,6 +21,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Decision tree", ++ // Short description. ++ "An implementation of an ID3-style decision tree for classification, which" ++ " supports categorical data. Given labeled data with numeric or " ++ "categorical features, a decision tree can be trained and saved; or, an " ++ "existing decision tree can be used for classification on new points.", ++ // Long description. + "Train and evaluate using a decision tree. Given a dataset containing " + "numeric or categorical features, and associated labels for each point in " + "the dataset, this program can train a decision tree on that data." +@@ -70,7 +76,15 @@ PROGRAM_INFO("Decision tree", + PRINT_DATASET("predictions") + ", one could call " + "\n\n" + + PRINT_CALL("decision_tree", "input_model", "tree", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("Decision stump", "#decision_stump"), ++ SEE_ALSO("Random forest", "#random_forest"), ++ SEE_ALSO("Decision trees on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_tree_learning"), ++ SEE_ALSO("Induction of Decision Trees (pdf)", ++ "https://link.springer.com/content/pdf/10.1007/BF00116251.pdf"), ++ SEE_ALSO("mlpack::tree::DecisionTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1DecisionTree.html")); + + // Datasets. + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", +diff --git a/src/mlpack/methods/det/CMakeLists.txt b/src/mlpack/methods/det/CMakeLists.txt +index 762c7b11c..58b402c7c 100644 +--- a/src/mlpack/methods/det/CMakeLists.txt ++++ b/src/mlpack/methods/det/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(det) + add_python_binding(det) ++add_markdown_docs(det "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp +index a9cb4ec47..07901d1bd 100644 +--- a/src/mlpack/methods/det/det_main.cpp ++++ b/src/mlpack/methods/det/det_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Density Estimation With Density Estimation Trees", ++ // Short description. ++ "An implementation of density estimation trees for the density estimation " ++ "task. Density estimation trees can be trained or used to predict the " ++ "density at locations given by query points.", ++ // Long description. + "This program performs a number of functions related to Density Estimation " + "Trees. The optimal Density Estimation Tree (DET) can be trained on a set " + "of data (specified by " + PRINT_PARAM_STRING("training") + ") using " +@@ -49,7 +54,15 @@ PROGRAM_INFO("Density Estimation With Density Estimation Trees", + "trained on the given training points, or a tree given as the parameter " + + PRINT_PARAM_STRING("input_model") + ". The density estimates for the test" + " points may be saved using the " + +- PRINT_PARAM_STRING("test_set_estimates") + " output parameter."); ++ PRINT_PARAM_STRING("test_set_estimates") + " output parameter.", ++ SEE_ALSO("Density estimation tree (DET) tutorial", ++ "@doxygen/dettutorial.html"), ++ SEE_ALSO("Density estimation on Wikipedia", ++ "https://en.wikipedia.org/wiki/Density_estimation"), ++ SEE_ALSO("Density estimation trees (pdf)", ++ "http://www.mlpack.org/papers/det.pdf"), ++ SEE_ALSO("mlpack::tree::DTree class documentation", ++ "@doxygen/classmlpack_1_1det_1_1DTree.html")); + + // Input data files. + PARAM_MATRIX_IN("training", "The data set on which to build a density " +diff --git a/src/mlpack/methods/emst/CMakeLists.txt b/src/mlpack/methods/emst/CMakeLists.txt +index 73cd431cb..7a073a8f9 100644 +--- a/src/mlpack/methods/emst/CMakeLists.txt ++++ b/src/mlpack/methods/emst/CMakeLists.txt +@@ -23,3 +23,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(emst) + add_python_binding(emst) ++add_markdown_docs(emst "cli;python" "geometry") +diff --git a/src/mlpack/methods/emst/emst_main.cpp b/src/mlpack/methods/emst/emst_main.cpp +index 3b0f9de98..e60ba7b64 100644 +--- a/src/mlpack/methods/emst/emst_main.cpp ++++ b/src/mlpack/methods/emst/emst_main.cpp +@@ -31,6 +31,10 @@ + #include "dtb.hpp" + + PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", ++ // Short description. ++ "An implementation of the Dual-Tree Boruvka algorithm for computing the " ++ "Euclidean minimum spanning tree of a set of input points.", ++ // Long description. + "This program can compute the Euclidean minimum spanning tree of a set of " + "input points using the dual-tree Boruvka algorithm." + "\n\n" +@@ -56,7 +60,14 @@ PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", + "The output matrix is a three-dimensional matrix, where each row indicates " + "an edge. The first dimension corresponds to the lesser index of the edge;" + " the second dimension corresponds to the greater index of the edge; and " +- "the third column corresponds to the distance between the two points."); ++ "the third column corresponds to the distance between the two points.", ++ SEE_ALSO("EMST Tutorial", "@doxygen/emst_tutorial.html"), ++ SEE_ALSO("Minimum spanning tree on Wikipedia", ++ "https://en.wikipedia.org/wiki/Minimum_spanning_tree"), ++ SEE_ALSO("Fast Euclidean Minimum Spanning Tree: Algorithm, Analysis, and " ++ "Applications (pdf)", "http://www.mlpack.org/papers/emst.pdf"), ++ SEE_ALSO("mlpack::emst::DualTreeBoruvka class documentation", ++ "@doxygen/classmlpack_1_1emst_1_1DualTreeBoruvka.html")); + + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); + PARAM_MATRIX_OUT("output", "Output data. Stored as an edge list.", "o"); +diff --git a/src/mlpack/methods/fastmks/CMakeLists.txt b/src/mlpack/methods/fastmks/CMakeLists.txt +index d9a6a6a8e..e6b6e55c1 100644 +--- a/src/mlpack/methods/fastmks/CMakeLists.txt ++++ b/src/mlpack/methods/fastmks/CMakeLists.txt +@@ -22,3 +22,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(fastmks) + add_python_binding(fastmks) ++add_markdown_docs(fastmks "cli;python" "geometry") +diff --git a/src/mlpack/methods/fastmks/fastmks_main.cpp b/src/mlpack/methods/fastmks/fastmks_main.cpp +index 1d8e7e7fb..fea273d65 100644 +--- a/src/mlpack/methods/fastmks/fastmks_main.cpp ++++ b/src/mlpack/methods/fastmks/fastmks_main.cpp +@@ -25,6 +25,12 @@ using namespace mlpack::metric; + using namespace mlpack::util; + + PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", ++ // Short description. ++ "An implementation of the single-tree and dual-tree fast max-kernel search" ++ " (FastMKS) algorithm. Given a set of reference points and a set of query" ++ " points, this can find the reference point with maximum kernel value for " ++ "each query point; trained models can be reused for future queries.", ++ // Long description. + "This program will find the k maximum kernels of a set of points, " + "using a query set and a reference set (which can optionally be the same " + "set). More specifically, for each point in the query set, the k points in" +@@ -51,7 +57,14 @@ PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", + "\n\n" + "This program performs FastMKS using a cover tree. The base used to build " + "the cover tree can be specified with the " + PRINT_PARAM_STRING("base") + +- " parameter."); ++ " parameter.", ++ SEE_ALSO("Fast max-kernel search tutorial (fastmks)", ++ "@doxygen/fmkstutorial.html"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Dual-tree Fast Exact Max-Kernel Search (pdf)", ++ "http://mlpack.org/papers/fmks.pdf"), ++ SEE_ALSO("mlpack::fastmks::FastMKS class documentation", ++ "@doxygen/classmlpack_1_1fastmks_1_1FastMKS.html")); + + // Model-building parameters. + PARAM_MATRIX_IN("reference", "The reference dataset.", "r"); +diff --git a/src/mlpack/methods/gmm/CMakeLists.txt b/src/mlpack/methods/gmm/CMakeLists.txt +index 74f9e8ace..964f490be 100644 +--- a/src/mlpack/methods/gmm/CMakeLists.txt ++++ b/src/mlpack/methods/gmm/CMakeLists.txt +@@ -23,7 +23,12 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(gmm_train) + add_python_binding(gmm_train) ++add_markdown_docs(gmm_train "cli;python" "clustering") ++ + add_cli_executable(gmm_generate) + add_python_binding(gmm_generate) ++add_markdown_docs(gmm_generate "cli;python" "clustering") ++ + add_cli_executable(gmm_probability) + add_python_binding(gmm_probability) ++add_markdown_docs(gmm_probability "cli;python" "clustering") +diff --git a/src/mlpack/methods/gmm/gmm_generate_main.cpp b/src/mlpack/methods/gmm/gmm_generate_main.cpp +index 784234941..4f3d10fdb 100644 +--- a/src/mlpack/methods/gmm/gmm_generate_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_generate_main.cpp +@@ -20,6 +20,10 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Sample Generator", ++ // Short description. ++ "A sample generator for pre-trained GMMs. Given a pre-trained GMM, this " ++ "can sample new points randomly from that distribution.", ++ // Long description. + "This program is able to generate samples from a pre-trained GMM (use " + "gmm_train to train a GMM). The pre-trained GMM must be specified with " + "the " + PRINT_PARAM_STRING("input_model") + " parameter. The number " +@@ -32,7 +36,13 @@ PROGRAM_INFO("GMM Sample Generator", + "samples in " + PRINT_DATASET("samples") + ":" + "\n\n" + + PRINT_CALL("gmm_generate", "input_model", "gmm", "samples", 100, "output", +- "samples")); ++ "samples"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM model to generate samples " + "from.", "m"); +diff --git a/src/mlpack/methods/gmm/gmm_probability_main.cpp b/src/mlpack/methods/gmm/gmm_probability_main.cpp +index f40c86628..6ba29585c 100644 +--- a/src/mlpack/methods/gmm/gmm_probability_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_probability_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Probability Calculator", ++ // Short description. ++ "A probability calculator for GMMs. Given a pre-trained GMM and a set of " ++ "points, this can compute the probability that each point is from the given" ++ " GMM.", ++ // Long description. + "This program calculates the probability that given points came from a " + "given GMM (that is, P(X | gmm)). The GMM is specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and the points are " +@@ -33,7 +38,13 @@ PROGRAM_INFO("GMM Probability Calculator", + PRINT_DATASET("probs") + ", the following command could be used:" + "\n\n" + + PRINT_CALL("gmm_probability", "input_model", "gmm", "input", "points", +- "output", "probs")); ++ "output", "probs"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM to use as model.", "m"); + PARAM_MATRIX_IN_REQ("input", "Input matrix to calculate probabilities of.", +diff --git a/src/mlpack/methods/gmm/gmm_train_main.cpp b/src/mlpack/methods/gmm/gmm_train_main.cpp +index c331d04dc..a01adfcb0 100644 +--- a/src/mlpack/methods/gmm/gmm_train_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_train_main.cpp +@@ -26,6 +26,11 @@ using namespace mlpack::kmeans; + using namespace std; + + PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", ++ // Short description. ++ "An implementation of the EM algorithm for training Gaussian mixture " ++ "models (GMMs). Given a dataset, this can train a GMM for future use " ++ "with other tools.", ++ // Long description. + "This program takes a parametric estimate of a Gaussian mixture model (GMM)" + " using the EM algorithm to find the maximum likelihood estimate. The " + "model may be saved and reused by other mlpack GMM tools." +@@ -84,7 +89,13 @@ PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", + ", the following command may be used: " + "\n\n" + + PRINT_CALL("gmm_train", "input_model", "gmm", "input", "data2", +- "gaussians", 6, "output_model", "new_gmm")); ++ "gaussians", 6, "output_model", "new_gmm"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + // Parameters for training. + PARAM_MATRIX_IN_REQ("input", "The training data on which the model will be " +diff --git a/src/mlpack/methods/hmm/CMakeLists.txt b/src/mlpack/methods/hmm/CMakeLists.txt +index 7add2baa3..7247430bb 100644 +--- a/src/mlpack/methods/hmm/CMakeLists.txt ++++ b/src/mlpack/methods/hmm/CMakeLists.txt +@@ -21,9 +21,16 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hmm_train) + add_python_binding(hmm_train) ++add_markdown_docs(hmm_train "cli;python" "misc. / other") ++ + add_cli_executable(hmm_loglik) + add_python_binding(hmm_loglik) ++add_markdown_docs(hmm_loglik "cli;python" "misc. / other") ++ + add_cli_executable(hmm_viterbi) + add_python_binding(hmm_viterbi) ++add_markdown_docs(hmm_viterbi "cli;python" "misc. / other") ++ + add_cli_executable(hmm_generate) + add_python_binding(hmm_generate) ++add_markdown_docs(hmm_generate "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/hmm/hmm_generate_main.cpp b/src/mlpack/methods/hmm/hmm_generate_main.cpp +index 0efc8dc84..cbfbe30bf 100644 +--- a/src/mlpack/methods/hmm/hmm_generate_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_generate_main.cpp +@@ -29,8 +29,13 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " +- "utility takes an already-trained HMM, specified as the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", ++ // Short description. ++ "A utility to generate random sequences from a pre-trained Hidden Markov " ++ "Model (HMM). The length of the desired sequence can be specified, and a " ++ "random sequence of observations is returned.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as the " + + PRINT_PARAM_STRING("model") + " parameter, and generates a random " + "observation sequence and hidden state sequence based on its parameters. " + "The observation sequence may be saved with the " + +@@ -47,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " + PRINT_DATASET("states") + ", the following command may be used: " + "\n\n" + + PRINT_CALL("hmm_generate", "model", "hmm", "length", 150, "output", +- "observations", "state", "states")); ++ "observations", "state", "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MODEL_IN_REQ(HMMModel, "model", "Trained HMM to generate sequences with.", + "m"); +diff --git a/src/mlpack/methods/hmm/hmm_loglik_main.cpp b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +index 6ebb3cbef..23a482603 100644 +--- a/src/mlpack/methods/hmm/hmm_loglik_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +@@ -26,8 +26,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " +- "utility takes an already-trained HMM, specified with the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", ++ // Short description. ++ "A utility for computing the log-likelihood of a sequence for Hidden Markov" ++ " Models (HMMs). Given a pre-trained HMM and an observation sequence, this" ++ " computes and returns the log-likelihood of that sequence being observed " ++ "from that HMM.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and evaluates the " + "log-likelihood of a sequence of observations, given with the " + + PRINT_PARAM_STRING("input") + " parameter. The computed log-likelihood is" +@@ -37,7 +43,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " + PRINT_DATASET("seq") + " with the pre-trained HMM " + PRINT_MODEL("hmm") + + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm")); ++ PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "File containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "File containing HMM.", "m"); +diff --git a/src/mlpack/methods/hmm/hmm_train_main.cpp b/src/mlpack/methods/hmm/hmm_train_main.cpp +index 109cfbddd..3e21085a1 100644 +--- a/src/mlpack/methods/hmm/hmm_train_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_train_main.cpp +@@ -27,9 +27,15 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " +- "Hidden Markov Model to be trained on labeled or unlabeled data. It " +- "support three types of HMMs: discrete HMMs, Gaussian HMMs, or GMM HMMs." ++PROGRAM_INFO("Hidden Markov Model (HMM) Training", ++ // Short description. ++ "An implementation of training algorithms for Hidden Markov Models (HMMs). " ++ "Given labeled or unlabeled data, an HMM can be trained for further use " ++ "with other mlpack HMM tools.", ++ // Long description. ++ "This program allows a Hidden Markov Model to be trained on labeled or " ++ "unlabeled data. It supports three types of HMMs: discrete HMMs, " ++ "Gaussian HMMs, or GMM HMMs." + "\n\n" + "Either one input sequence can be specified (with --input_file), or, a " + "file containing files in which input sequences can be found (when " +@@ -46,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " + "\n\n" + "Optionally, a pre-created HMM model can be used as a guess for the " + "transition matrix and emission probabilities; this is specifiable with " +- "--model_file."); ++ "--model_file.", ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_STRING_IN_REQ("input_file", "File containing input observations.", "i"); + PARAM_STRING_IN("type", "Type of HMM: discrete | gaussian | gmm.", "t", +diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +index d0e1168b3..1fa3c5f58 100644 +--- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +@@ -27,8 +27,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " +- "utility takes an already-trained HMM, specified as " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", ++ // Short description. ++ "A utility for computing the most probable hidden state sequence for Hidden" ++ " Markov Models (HMMs). Given a pre-trained HMM and an observed sequence, " ++ "this uses the Viterbi algorithm to compute and return the most probable " ++ "hidden state sequence.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as " + + PRINT_PARAM_STRING("input_model") + ", and evaluates the most probable " + "hidden state sequence of a given sequence of observations (specified as " + "'" + PRINT_PARAM_STRING("input") + ", using the Viterbi algorithm. The " +@@ -41,7 +47,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " + ", the following command could be used:" + "\n\n" + + PRINT_CALL("hmm_viterbi", "input", "obs", "input_model", "hmm", "output", +- "states")); ++ "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "Matrix containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "Trained HMM to use.", "m"); +diff --git a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +index b573a19c6..2a94415b3 100644 +--- a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt ++++ b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +@@ -30,3 +30,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hoeffding_tree) + add_python_binding(hoeffding_tree) ++add_markdown_docs(hoeffding_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +index 2c4576337..825b7c1f4 100644 +--- a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp ++++ b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +@@ -26,6 +26,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Hoeffding trees", ++ // Short description. ++ "An implementation of Hoeffding trees, a form of streaming decision tree " ++ "for classification. Given labeled data, a Hoeffding tree can be trained " ++ "and saved for later use, or a pre-trained Hoeffding tree can be used for " ++ "predicting the classifications of new points.", ++ // Long description. + "This program implements Hoeffding trees, a form of streaming decision tree" + " suited best for large (or streaming) datasets. This program supports " + "both categorical and numeric data. Given an input dataset, this program " +@@ -70,7 +76,13 @@ PROGRAM_INFO("Hoeffding trees", + PRINT_DATASET("class_probs") + " with the following command: " + "\n\n" + + PRINT_CALL("hoeffding_tree", "input_model", "tree", "test", "test_set", +- "predictions", "predictions", "probabilities", "class_probs")); ++ "predictions", "predictions", "probabilities", "class_probs"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Mining High-Speed Data Streams (pdf)", ++ "http://dm.cs.washington.edu/papers/vfdt-kdd00.pdf"), ++ SEE_ALSO("mlpack::tree::HoeffdingTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1HoeffdingTree.html")); + + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", + "t"); +diff --git a/src/mlpack/methods/kernel_pca/CMakeLists.txt b/src/mlpack/methods/kernel_pca/CMakeLists.txt +index c14a9f3ec..9bd0cd3a0 100644 +--- a/src/mlpack/methods/kernel_pca/CMakeLists.txt ++++ b/src/mlpack/methods/kernel_pca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kernel_pca) + add_python_binding(kernel_pca) ++add_markdown_docs(kernel_pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +index 7ca626812..af6c5c17f 100644 +--- a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp ++++ b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +@@ -41,6 +41,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Kernel Principal Components Analysis", ++ // Short description. ++ "An implementation of Kernel Principal Components Analysis (KPCA). This " ++ "can be used to perform nonlinear dimensionality reduction or preprocessing" ++ " on a given dataset.", ++ // Long description. + "This program performs Kernel Principal Components Analysis (KPCA) on the " + "specified dataset with the specified kernel. This will transform the " + "data onto the kernel principal components, and optionally reduce the " +@@ -93,7 +98,13 @@ PROGRAM_INFO("Kernel Principal Components Analysis", + "the kernel matrix; to specify the sampling scheme, the " + + PRINT_PARAM_STRING("sampling") + " parameter is used. The " + "sampling scheme for the Nystr\u00F6m method can be chosen from the " +- "following list: 'kmeans', 'random', 'ordered'."); ++ "following list: 'kmeans', 'random', 'ordered'.", ++ SEE_ALSO("Kernel principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Kernel_principal_component_analysis"), ++ SEE_ALSO("Kernel Principal Component Analysis (pdf)", ++ "http://pca.narod.ru/scholkopf_kernel.pdf"), ++ SEE_ALSO("mlpack::kpca::KernelPCA class documentation", ++ "@doxygen/classmlpack_1_1kpca_1_1KernelPCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform KPCA on.", "i"); + PARAM_MATRIX_OUT("output", "Matrix to save modified dataset to.", "o"); +@@ -107,9 +118,9 @@ PARAM_INT_IN("new_dimensionality", "If not 0, reduce the dimensionality of " + PARAM_FLAG("center", "If set, the transformed data will be centered about the " + "origin.", "c"); + +-PARAM_FLAG("nystroem_method", "If set, the nystroem method will be used.", "n"); ++PARAM_FLAG("nystroem_method", "If set, the Nystroem method will be used.", "n"); + +-PARAM_STRING_IN("sampling", "Sampling scheme to use for the nystroem method: " ++PARAM_STRING_IN("sampling", "Sampling scheme to use for the Nystroem method: " + "'kmeans', 'random', 'ordered'", "s", "kmeans"); + + PARAM_DOUBLE_IN("kernel_scale", "Scale, for 'hyptan' kernel.", "S", 1.0); +diff --git a/src/mlpack/methods/kmeans/CMakeLists.txt b/src/mlpack/methods/kmeans/CMakeLists.txt +index bf4990083..9cfec0fdc 100644 +--- a/src/mlpack/methods/kmeans/CMakeLists.txt ++++ b/src/mlpack/methods/kmeans/CMakeLists.txt +@@ -40,3 +40,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kmeans) + add_python_binding(kmeans) ++add_markdown_docs(kmeans "cli;python" "clustering") +diff --git a/src/mlpack/methods/kmeans/kmeans_main.cpp b/src/mlpack/methods/kmeans/kmeans_main.cpp +index b0319ccb5..9d3f40c7d 100644 +--- a/src/mlpack/methods/kmeans/kmeans_main.cpp ++++ b/src/mlpack/methods/kmeans/kmeans_main.cpp +@@ -28,11 +28,17 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " +- "on the given dataset. It can return the learned cluster assignments, and " +- "the centroids of the clusters. Empty clusters are not allowed by default;" +- " when a cluster becomes empty, the point furthest from the centroid of the" +- " cluster with maximum variance is taken to fill that cluster." ++PROGRAM_INFO("K-Means Clustering", ++ // Short description. ++ "An implementation of several strategies for efficient k-means clustering. " ++ "Given a dataset and a value of k, this computes and returns a k-means " ++ "clustering on that data.", ++ // Long description. ++ "This program performs K-Means clustering on the given dataset. It can " ++ "return the learned cluster assignments, and the centroids of the clusters." ++ " Empty clusters are not allowed by default; when a cluster becomes empty," ++ " the point furthest from the centroid of the cluster with maximum variance" ++ " is taken to fill that cluster." + "\n\n" + "Optionally, the Bradley and Fayyad approach (\"Refining initial points for" + " k-means clustering\", 1998) can be used to select initial points by " +@@ -85,7 +91,21 @@ PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " + "following command may be used:" + "\n\n" + + PRINT_CALL("kmeans", "input", "data", "initial_centroids", "initial", +- "clusters", 10, "max_iterations", 500, "centroid", "final")); ++ "clusters", 10, "max_iterations", 500, "centroid", "final"), ++ SEE_ALSO("K-Means tutorial", "@doxygen/kmtutorial.html"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Using the triangle inequality to accelerate k-means (pdf)", ++ "http://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf"), ++ SEE_ALSO("Making k-means even faster (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.586.2554" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("Accelerating exact k-means algorithms with geometric reasoning " ++ "(pdf)", "http://reports-archive.adm.cs.cmu.edu/anon/anon/usr/ftp/" ++ "usr0/ftp/2000/CMU-CS-00-105.pdf"), ++ SEE_ALSO("A dual-tree algorithm for fast k-means clustering with large k " ++ "(pdf)", "http://www.ratml.org/pub/pdf/2017dual.pdf"), ++ SEE_ALSO("mlpack::kmeans::KMeans class documentation", ++ "@doxygen/classmlpack_1_1kmeans_1_1KMeans.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/lars/CMakeLists.txt b/src/mlpack/methods/lars/CMakeLists.txt +index 1388f24be..7e4cb7890 100644 +--- a/src/mlpack/methods/lars/CMakeLists.txt ++++ b/src/mlpack/methods/lars/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(lars) + add_python_binding(lars) ++add_markdown_docs(lars "cli;python" "regression") +diff --git a/src/mlpack/methods/lars/lars_main.cpp b/src/mlpack/methods/lars/lars_main.cpp +index 040c5f0c3..e0b2f38f3 100644 +--- a/src/mlpack/methods/lars/lars_main.cpp ++++ b/src/mlpack/methods/lars/lars_main.cpp +@@ -21,10 +21,16 @@ using namespace mlpack; + using namespace mlpack::regression; + using namespace mlpack::util; + +-PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " +- "(Stagewise/laSso). This is a stage-wise homotopy-based algorithm for " +- "L1-regularized linear regression (LASSO) and L1+L2-regularized linear " +- "regression (Elastic Net)." ++PROGRAM_INFO("LARS", ++ // Short description. ++ "An implementation of Least Angle Regression (Stagewise/laSso), also known" ++ " as LARS. This can train a LARS/LASSO/Elastic Net model and use that " ++ "model or a pre-trained model to output regression predictions for a test " ++ "set.", ++ // Long description. ++ "An implementation of LARS: Least Angle Regression (Stagewise/laSso). " ++ "This is a stage-wise homotopy-based algorithm for L1-regularized linear " ++ "regression (LASSO) and L1+L2-regularized linear regression (Elastic Net)." + "\n\n" + "This program is able to train a LARS/LASSO/Elastic Net model or load a " + "model from file, output regression predictions for a test set, and save " +@@ -80,7 +86,12 @@ PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " + "and save those responses to " + PRINT_DATASET("test_predictions") + ": " + "\n\n" + + PRINT_CALL("lars", "input_model", "lasso_model", "test", "test", +- "output_predictions", "test_predictions")); ++ "output_predictions", "test_predictions"), ++ SEE_ALSO("@linear_regression", "#linear_regression"), ++ SEE_ALSO("Least angle regression (pdf)", ++ "http://mlpack.org/papers/lars.pdf"), ++ SEE_ALSO("mlpack::regression::LARS C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LARS.html")); + + PARAM_TMATRIX_IN("input", "Matrix of covariates (X).", "i"); + PARAM_MATRIX_IN("responses", "Matrix of responses/observations (y).", "r"); +diff --git a/src/mlpack/methods/linear_regression/CMakeLists.txt b/src/mlpack/methods/linear_regression/CMakeLists.txt +index 58a7426e8..b4e63cdca 100644 +--- a/src/mlpack/methods/linear_regression/CMakeLists.txt ++++ b/src/mlpack/methods/linear_regression/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(linear_regression) + add_python_binding(linear_regression) ++add_markdown_docs(linear_regression "cli;python" "regression") +diff --git a/src/mlpack/methods/linear_regression/linear_regression_main.cpp b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +index c1e33cd06..1cb58a85c 100644 +--- a/src/mlpack/methods/linear_regression/linear_regression_main.cpp ++++ b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +@@ -22,6 +22,12 @@ using namespace arma; + using namespace std; + + PROGRAM_INFO("Simple Linear Regression and Prediction", ++ // Short description. ++ "An implementation of simple linear regression and ridge regression using " ++ "ordinary least squares. Given a dataset and responses, a model can be " ++ "trained and saved for later use, or a pre-trained model can be used to " ++ "output regression predictions for a test set.", ++ // Long description. + "An implementation of simple linear regression and simple ridge regression " + "using ordinary least squares. This solves the problem" + "\n\n" +@@ -63,7 +69,13 @@ PROGRAM_INFO("Simple Linear Regression and Prediction", + "used:" + "\n\n" + + PRINT_CALL("linear_regression", "input_model", "lr_model", "test", "X_test", +- "output_predictions", "X_test_responses")); ++ "output_predictions", "X_test_responses"), ++ SEE_ALSO("Linear/ridge regression tutorial", "@doxygen/lrtutorial.html"), ++ SEE_ALSO("@lars", "#lars"), ++ SEE_ALSO("Linear regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Linear_regression"), ++ SEE_ALSO("mlpack::regression::LinearRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LinearRegression.html")); + + PARAM_MATRIX_IN("training", "Matrix containing training set X (regressors).", + "t"); +diff --git a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +index 6720525c7..d4b498e55 100644 +--- a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt ++++ b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(local_coordinate_coding) + add_python_binding(local_coordinate_coding) ++add_markdown_docs(local_coordinate_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +index f0ee7e6dc..16d8e13d8 100644 +--- a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp ++++ b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +@@ -24,6 +24,12 @@ using namespace mlpack::sparse_coding; // For NothingInitializer. + using namespace mlpack::util; + + PROGRAM_INFO("Local Coordinate Coding", ++ // Short description. ++ "An implementation of Local Coordinate Coding (LCC), a data transformation " ++ "technique. Given input data, this transforms each point to be expressed " ++ "as a linear combination of a few points in the dataset; once an LCC model " ++ "is trained, it can be used to transform points later also.", ++ // Long description. + "An implementation of Local Coordinate Coding (LCC), which " + "codes data that approximately lives on a manifold using a variation of l1-" + "norm regularized sparse coding. Given a dense data matrix X with n points" +@@ -67,7 +73,13 @@ PROGRAM_INFO("Local Coordinate Coding", + "be used:" + "\n\n" + + PRINT_CALL("local_coordinate_coding", "input_model", "lcc_model", "test", +- "points", "codes", "new_codes")); ++ "points", "codes", "new_codes"), ++ SEE_ALSO("@sparse_coding", "#sparse_coding"), ++ SEE_ALSO("Nonlinear learning using local coordinate coding (pdf)", ++ "https://papers.nips.cc/paper/3875-nonlinear-learning-using-local-" ++ "coordinate-coding.pdf"), ++ SEE_ALSO("mlpack::lcc::LocalCoordinateCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1lcc_1_1LocalCoordinateCoding.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +diff --git a/src/mlpack/methods/logistic_regression/CMakeLists.txt b/src/mlpack/methods/logistic_regression/CMakeLists.txt +index d7edd6ba1..673e214c6 100644 +--- a/src/mlpack/methods/logistic_regression/CMakeLists.txt ++++ b/src/mlpack/methods/logistic_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(logistic_regression) + add_python_binding(logistic_regression) ++add_markdown_docs(logistic_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +index 2a0ed23a7..385525ab0 100644 +--- a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp ++++ b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +@@ -24,6 +24,11 @@ using namespace mlpack::optimization; + using namespace mlpack::util; + + PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", ++ // Short description. ++ "An implementation of L2-regularized logistic regression for two-class " ++ "classification. Given labeled data, a model can be trained and saved for " ++ "future use; or, a pre-trained model can be used to classify new points.", ++ // Long description. + "An implementation of L2-regularized logistic regression using either the " + "L-BFGS optimizer or SGD (stochastic gradient descent). This solves the " + "regression problem" +@@ -39,8 +44,8 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + "those things at once. In addition, this program allows classification on " + "a test dataset (specified with the " + PRINT_PARAM_STRING("test") + " " + "parameter) and the classification results may be saved with the " + +- PRINT_PARAM_STRING("output") + " output parameter. The trained logistic " +- "regression model may be saved using the " + ++ PRINT_PARAM_STRING("output") + " output parameter." ++ " The trained logistic regression model may be saved using the " + + PRINT_PARAM_STRING("output_model") + " output parameter." + "\n\n" + "The training data, if specified, may have class labels as its last " +@@ -95,7 +100,13 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + PRINT_DATASET("predictions") + "', the following command may be used: " + "\n\n" + + PRINT_CALL("logistic_regression", "input_model", "lr_model", "test", "test", +- "output", "predictions")); ++ "output", "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Logistic regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Logistic_regression"), ++ SEE_ALSO("mlpack::regression::LogisticRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LogisticRegression.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/lsh/CMakeLists.txt b/src/mlpack/methods/lsh/CMakeLists.txt +index 758a2152b..4ec7baacf 100644 +--- a/src/mlpack/methods/lsh/CMakeLists.txt ++++ b/src/mlpack/methods/lsh/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # sets with p-stable LSH. + add_cli_executable(lsh) + add_python_binding(lsh) ++add_markdown_docs(lsh "cli;python" "geometry") +diff --git a/src/mlpack/methods/lsh/lsh_main.cpp b/src/mlpack/methods/lsh/lsh_main.cpp +index fdf34d3cf..840fc43e4 100644 +--- a/src/mlpack/methods/lsh/lsh_main.cpp ++++ b/src/mlpack/methods/lsh/lsh_main.cpp +@@ -25,6 +25,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", ++ // Short description. ++ "An implementation of approximate k-nearest-neighbor search with " ++ "locality-sensitive hashing (LSH). Given a set of reference points and a " ++ "set of query points, this will compute the k approximate nearest neighbors" ++ " of each query point in the reference set; models can be saved for future " ++ "use.", ++ // Long description. + "This program will calculate the k approximate-nearest-neighbors of a set " + "of points using locality-sensitive hashing. You may specify a separate set" + " of reference points and query points, or just a reference set which will " +@@ -49,7 +56,15 @@ PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", + " parameter can be specified to set the random seed." + "\n\n" + "This program also has many other parameters to control its functionality;" +- " see the parameter-specific documentation for more information."); ++ " see the parameter-specific documentation for more information.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("Locality-sensitive hashing on Wikipedia", ++ "https://en.wikipedia.org/wiki/Locality-sensitive_hashing"), ++ SEE_ALSO("Locality-sensitive hashing scheme based on p-stable distributions" ++ " (pdf)", "http://mlpack.org/papers/lsh.pdf"), ++ SEE_ALSO("mlpack::neighbor::LSHSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1LSHSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/mean_shift/CMakeLists.txt b/src/mlpack/methods/mean_shift/CMakeLists.txt +index 1087ab532..5aced5225 100644 +--- a/src/mlpack/methods/mean_shift/CMakeLists.txt ++++ b/src/mlpack/methods/mean_shift/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(mean_shift) + add_python_binding(mean_shift) ++add_markdown_docs(mean_shift "cli;python" "clustering") +diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +index 4710ccaad..ac7f17e18 100644 +--- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp ++++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +@@ -23,9 +23,15 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " +- "clustering on the given dataset, storing the learned cluster assignments " +- "either as a column of labels in the input dataset or separately." ++PROGRAM_INFO("Mean Shift Clustering", ++ // Short description. ++ "A fast implementation of mean-shift clustering using dual-tree range " ++ "search. Given a dataset, this uses the mean shift algorithm to produce " ++ "and return a clustering of the data.", ++ // Long description. ++ "This program performs mean shift clustering on the given dataset, storing " ++ "the learned cluster assignments either as a column of labels in the input " ++ "dataset or separately." + "\n\n" + "The input dataset should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter, and the radius used for search" +@@ -42,7 +48,16 @@ PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " + PRINT_DATASET("data") + " and store the centroids to " + + PRINT_DATASET("centroids") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids")); ++ PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids"), ++ SEE_ALSO("@kmeans", "#kmeans"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Mean shift on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mean_shift"), ++ SEE_ALSO("Mean Shift, Mode Seeking, and Clustering (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.510.1222" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::mean_shift::MeanShift C++ class documentation", ++ "@doxygen/classmlpack_1_1meanshift_1_1MeanShift.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/naive_bayes/CMakeLists.txt b/src/mlpack/methods/naive_bayes/CMakeLists.txt +index f15dc229c..b2afe0707 100644 +--- a/src/mlpack/methods/naive_bayes/CMakeLists.txt ++++ b/src/mlpack/methods/naive_bayes/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nbc) + add_python_binding(nbc) ++add_markdown_docs(nbc "cli;python" "classification") +diff --git a/src/mlpack/methods/naive_bayes/nbc_main.cpp b/src/mlpack/methods/naive_bayes/nbc_main.cpp +index 68a4a70e0..612d675d0 100644 +--- a/src/mlpack/methods/naive_bayes/nbc_main.cpp ++++ b/src/mlpack/methods/naive_bayes/nbc_main.cpp +@@ -26,6 +26,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Parametric Naive Bayes Classifier", ++ // Short description. ++ "An implementation of the Naive Bayes Classifier, used for classification. " ++ "Given labeled data, an NBC model can be trained and saved, or, a " ++ "pre-trained model can be used for classification.", ++ // Long description. + "This program trains the Naive Bayes classifier on the given labeled " + "training set, or loads a model from the given model file, and then may use" + " that trained model to classify the points in a given test set." +@@ -66,7 +71,14 @@ PROGRAM_INFO("Parametric Naive Bayes Classifier", + "may be used:" + "\n\n" + + PRINT_CALL("nbc", "input_model", "nbc_model", "test", "test_set", "output", +- "predictions")); ++ "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Naive Bayes classifier on Wikipedia", ++ "https://en.wikipedia.org/wiki/Naive_Bayes_classifier"), ++ SEE_ALSO("mlpack::naive_bayes::NaiveBayesClassifier C++ class " ++ "documentation", "@doxygen/classmlpack_1_1naive__bayes_1_1" ++ "NaiveBayesClassifier.html")); + + // A struct for saving the model with mappings. + struct NBCModel +diff --git a/src/mlpack/methods/nca/CMakeLists.txt b/src/mlpack/methods/nca/CMakeLists.txt +index 777eef39b..7b1fd98f5 100644 +--- a/src/mlpack/methods/nca/CMakeLists.txt ++++ b/src/mlpack/methods/nca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nca) + add_python_binding(nca) ++add_markdown_docs(nca "cli;python" "transformations") +diff --git a/src/mlpack/methods/nca/nca_main.cpp b/src/mlpack/methods/nca/nca_main.cpp +index cc988f128..44e9d4525 100644 +--- a/src/mlpack/methods/nca/nca_main.cpp ++++ b/src/mlpack/methods/nca/nca_main.cpp +@@ -22,6 +22,12 @@ + + // Define parameters. + PROGRAM_INFO("Neighborhood Components Analysis (NCA)", ++ // Short description. ++ "An implementation of neighborhood components analysis, a distance learning" ++ " technique that can be used for preprocessing. Given a labeled dataset, " ++ "this uses NCA, which seeks to improve the k-nearest-neighbor " ++ "classification, and returns the learned distance metric.", ++ // Long description. + "This program implements Neighborhood Components Analysis, both a linear " + "dimensionality reduction technique and a distance learning technique. The" + " method seeks to improve k-nearest-neighbor classification on a dataset " +@@ -82,7 +88,14 @@ PROGRAM_INFO("Neighborhood Components Analysis (NCA)", + "mlpack L-BFGS documentation (in lbfgs.hpp) or the vast set of published " + "literature on L-BFGS." + "\n\n" +- "By default, the SGD optimizer is used."); ++ "By default, the SGD optimizer is used.", ++ SEE_ALSO("Neighbourhood components analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Neighbourhood_components_analysis"), ++ SEE_ALSO("Neighbourhood components analysis (pdf)", ++ "http://papers.nips.cc/paper/2566-neighbourhood-components-" ++ "analysis.pdf"), ++ SEE_ALSO("mlpack::nca::NCA C++ class documentation", ++ "@doxygen/classmlpack_1_1nca_1_1NCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to run NCA on.", "i"); + PARAM_MATRIX_OUT("output", "Output matrix for learned distance matrix.", "o"); +diff --git a/src/mlpack/methods/neighbor_search/CMakeLists.txt b/src/mlpack/methods/neighbor_search/CMakeLists.txt +index 1958ebe17..789beb295 100644 +--- a/src/mlpack/methods/neighbor_search/CMakeLists.txt ++++ b/src/mlpack/methods/neighbor_search/CMakeLists.txt +@@ -29,5 +29,8 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # Add mlpack_knn and mlpack_kfn executables. + add_cli_executable(knn) + add_python_binding(knn) ++add_markdown_docs(knn "cli;python" "geometry") ++ + add_cli_executable(kfn) + add_python_binding(kfn) ++add_markdown_docs(kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp +index 56415c3ae..57e2ffdf6 100644 +--- a/src/mlpack/methods/neighbor_search/kfn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp +@@ -34,6 +34,12 @@ typedef NSModel KFNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Furthest-Neighbors Search", ++ // Short description. ++ "An implementation of k-furthest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k furthest neighbors in the reference set of each query" ++ " point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-furthest-neighbors of a set of " + "points. You may specify a separate set of reference points and query " + "points, or just a reference set which will be used as both the reference " +@@ -51,7 +57,13 @@ PROGRAM_INFO("k-Furthest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th furthest neighbor from the point in the " + "query set with index i. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@approx_kfn", "#approx_kfn"), ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp +index 336315dea..46a2b53bf 100644 +--- a/src/mlpack/methods/neighbor_search/knn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/knn_main.cpp +@@ -36,6 +36,12 @@ typedef NSModel KNNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Nearest-Neighbors Search", ++ // Short description. ++ "An implementation of k-nearest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k nearest neighbors in the reference set of each query " ++ "point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-nearest-neighbors of a set of " + "points using kd-trees or cover trees (cover tree support is experimental " + "and may be slow). You may specify a separate set of " +@@ -53,7 +59,16 @@ PROGRAM_INFO("k-Nearest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th nearest neighbor from the point in the " + "query set with index i. Row j and column i in the distances output matrix" +- " corresponds to the distance between those two points."); ++ " corresponds to the distance between those two points.", ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("@kfn", "#kfn"), ++ SEE_ALSO("NeighborSearch tutorial (k-nearest-neighbors)", ++ "@doxygen/nstutorial.html"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/nmf/CMakeLists.txt b/src/mlpack/methods/nmf/CMakeLists.txt +index 18e01f1b4..f1c4666f3 100644 +--- a/src/mlpack/methods/nmf/CMakeLists.txt ++++ b/src/mlpack/methods/nmf/CMakeLists.txt +@@ -1,2 +1,3 @@ + add_cli_executable(nmf) + add_python_binding(nmf) ++add_markdown_docs(nmf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/nmf/nmf_main.cpp b/src/mlpack/methods/nmf/nmf_main.cpp +index e39273eb2..1e9e4a539 100644 +--- a/src/mlpack/methods/nmf/nmf_main.cpp ++++ b/src/mlpack/methods/nmf/nmf_main.cpp +@@ -28,10 +28,15 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " +- "non-negative matrix factorization on the given dataset, storing the " +- "resulting decomposed matrices in the specified files. For an input " +- "dataset V, NMF decomposes V into two matrices W and H such that " ++PROGRAM_INFO("Non-negative Matrix Factorization", ++ // Short description. ++ "An implementation of non-negative matrix factorization. This can be used " ++ "to decompose an input dataset into two low-rank non-negative components.", ++ // Long description. ++ "This program performs non-negative matrix factorization on the given " ++ "dataset, storing the resulting decomposed matrices in the specified " ++ "files. For an input dataset V, NMF decomposes V into two matrices W " ++ "and H such that " + "\n\n" + "V = W * H" + "\n\n" +@@ -60,7 +65,17 @@ PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " + PRINT_DATASET("H") + ", the following command could be used: " + "\n\n" + + PRINT_CALL("nmf", "input", "V", "w", "W", "h", "H", "rank", 10, +- "update_rules", "multdist")); ++ "update_rules", "multdist"), ++ SEE_ALSO("@cf", "#cf"), ++ SEE_ALSO("Alternating matrix factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Non-negative matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Non-negative_matrix_factorization"), ++ SEE_ALSO("Algorithms for non-negative matrix factorization (pdf)", ++ "http://papers.nips.cc/paper/1861-algorithms-for-non-negative-matrix-" ++ "factorization.pdf"), ++ SEE_ALSO("mlpack::amf::AMF C++ class documentation", ++ "@doxygen/classmlpack_1_1amf_1_1AMF.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform NMF on.", "i"); +diff --git a/src/mlpack/methods/pca/CMakeLists.txt b/src/mlpack/methods/pca/CMakeLists.txt +index 097598ee8..8b317c4fc 100644 +--- a/src/mlpack/methods/pca/CMakeLists.txt ++++ b/src/mlpack/methods/pca/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(pca) + add_python_binding(pca) ++add_markdown_docs(pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/pca/pca_main.cpp b/src/mlpack/methods/pca/pca_main.cpp +index 4176f0680..493a80026 100644 +--- a/src/mlpack/methods/pca/pca_main.cpp ++++ b/src/mlpack/methods/pca/pca_main.cpp +@@ -26,12 +26,18 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Principal Components Analysis", "This program performs principal " +- "components analysis on the given dataset using the exact, randomized, " +- "randomized block Krylov, or QUIC SVD method. It will transform the data " +- "onto its principal components, optionally performing dimensionality " +- "reduction by ignoring the principal components with the smallest " +- "eigenvalues." ++PROGRAM_INFO("Principal Components Analysis", ++ // Short description. ++ "An implementation of several strategies for principal components analysis " ++ "(PCA), a common preprocessing step. Given a dataset and a desired new " ++ "dimensionality, this can reduce the dimensionality of the data using the " ++ "linear transformation determined by PCA.", ++ // Long description. ++ "This program performs principal components analysis on the given dataset " ++ "using the exact, randomized, randomized block Krylov, or QUIC SVD method. " ++ "It will transform the data onto its principal components, optionally " ++ "performing dimensionality reduction by ignoring the principal components " ++ "with the smallest eigenvalues." + "\n\n" + "Use the " + PRINT_PARAM_STRING("input") + " parameter to specify the " + "dataset to perform PCA on. A desired new dimensionality can be specified " +@@ -52,7 +58,11 @@ PROGRAM_INFO("Principal Components Analysis", "This program performs principal " + PRINT_DATASET("data_mod") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("pca", "input", "data", "new_dimensionality", 5, +- "decomposition_method", "randomized", "output", "data_mod")); ++ "decomposition_method", "randomized", "output", "data_mod"), ++ SEE_ALSO("Principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Principal_component_analysis"), ++ SEE_ALSO("mlpack::pca::PCA C++ class documentation", ++ "@doxygen/classmlpack_1_1pca_1_1PCA.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform PCA on.", "i"); +diff --git a/src/mlpack/methods/perceptron/CMakeLists.txt b/src/mlpack/methods/perceptron/CMakeLists.txt +index f0b4afc59..96a003562 100644 +--- a/src/mlpack/methods/perceptron/CMakeLists.txt ++++ b/src/mlpack/methods/perceptron/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(perceptron) + add_python_binding(perceptron) ++add_markdown_docs(perceptron "cli;python" "classification") +diff --git a/src/mlpack/methods/perceptron/perceptron_main.cpp b/src/mlpack/methods/perceptron/perceptron_main.cpp +index 524a758ab..1127e9058 100644 +--- a/src/mlpack/methods/perceptron/perceptron_main.cpp ++++ b/src/mlpack/methods/perceptron/perceptron_main.cpp +@@ -26,6 +26,12 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Perceptron", ++ // Short description. ++ "An implementation of a perceptron---a single level neural network--=for " ++ "classification. Given labeled data, a perceptron can be trained and saved" ++ " for future use; or, a pre-trained perceptron can be used for " ++ "classification on new points.", ++ // Long description. + "This program implements a perceptron, which is a single level neural " + "network. The perceptron makes its predictions based on a linear predictor " + "function combining a set of weights with the feature vector. The " +@@ -75,7 +81,12 @@ PROGRAM_INFO("Perceptron", + "cannot pass a perceptron model trained on 2 classes and then re-train with" + " a 4-class dataset. Similarly, attempting classification on a " + "3-dimensional dataset with a perceptron that has been trained on 8 " +- "dimensions will cause an error."); ++ "dimensions will cause an error.", ++ SEE_ALSO("@adaboost", "#adaboost"), ++ SEE_ALSO("Perceptron on Wikipedia", ++ "https://en.wikipedia.org/wiki/Perceptron"), ++ SEE_ALSO("mlpack::perceptron::Perceptron C++ class documentation", ++ "@doxygen/classmlpack_1_1perceptron_1_1Perceptron.html")); + + // When we save a model, we must also save the class mappings. So we use this + // auxiliary structure to store both the perceptron and the mapping, and we'll +diff --git a/src/mlpack/methods/preprocess/CMakeLists.txt b/src/mlpack/methods/preprocess/CMakeLists.txt +index af9299c64..a65487981 100644 +--- a/src/mlpack/methods/preprocess/CMakeLists.txt ++++ b/src/mlpack/methods/preprocess/CMakeLists.txt +@@ -15,10 +15,17 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + #add_cli_executable(preprocess_stats) + add_cli_executable(preprocess_split) + add_python_binding(preprocess_split) ++add_markdown_docs(preprocess_split "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_binarize) + add_python_binding(preprocess_binarize) ++add_markdown_docs(preprocess_binarize "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_describe) + add_python_binding(preprocess_describe) ++add_markdown_docs(preprocess_describe "cli;python" "preprocessing") ++ + #add_cli_executable(preprocess_scan) + add_cli_executable(preprocess_imputer) + #add_python_binding(preprocess_imputer) ++add_markdown_docs(preprocess_imputer "cli" "preprocessing") +diff --git a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +index 53e6fb056..9d69834af 100644 +--- a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +@@ -14,7 +14,13 @@ + #include + #include + +-PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " ++PROGRAM_INFO("Binarize Data", ++ // Short description. ++ "A utility to binarize a dataset. Given a dataset, this utility converts " ++ "each value in the desired dimension(s) to 0 or 1; this can be a useful " ++ "preprocessing step.", ++ // Long description. ++ "This utility takes a dataset and binarizes the " + "variables into either 0 or 1 given threshold. User can apply binarization " + "on a dimension or the whole dataset. The dimension to apply binarization " + "to can be specified using the " + PRINT_PARAM_STRING("dimension") + +@@ -38,7 +44,10 @@ PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " + PRINT_DATASET("X") + ", we could instead run" + "\n\n" + + PRINT_CALL("preprocess_binarize", "input", "X", "threshold", 5.0, +- "dimension", 0, "output", "Y")); ++ "dimension", 0, "output", "Y"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +index 744f826a5..43b6df7f5 100644 +--- a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +@@ -22,12 +22,17 @@ using namespace mlpack::util; + using namespace std; + using namespace boost; + +-PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " +- "prints out the descriptive statistics of the data. Descriptive statistics " +- "is the discipline of quantitatively describing the main features of a " +- "collection of information, or the quantitative description itself. The " +- "program does not modify the original file, but instead prints out the " +- "statistics to the console. The printed result will look like a table." ++PROGRAM_INFO("Descriptive Statistics", ++ // Short description. ++ "A utility for printing descriptive statistics about a dataset. This " ++ "prints a number of details about a dataset in a tabular format.", ++ // Long description. ++ "This utility takes a dataset and prints out the descriptive statistics " ++ "of the data. Descriptive statistics is the discipline of quantitatively " ++ "describing the main features of a collection of information, or the " ++ "quantitative description itself. The program does not modify the original " ++ "file, but instead prints out the statistics to the console. The printed " ++ "result will look like a table." + "\n\n" + "Optionally, width and precision of the output can be adjusted by a user " + "using the " + PRINT_PARAM_STRING("width") + " and " + +@@ -47,7 +52,10 @@ PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " + "the dataset as a population, we could run" + "\n\n" + + PRINT_CALL("preprocess_describe", "input", "X", "width", 10, "precision", 5, +- "verbose", true)); ++ "verbose", true), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data,", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +index 848bbe9cf..1825334ad 100644 +--- a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +@@ -23,8 +23,14 @@ + #include + #include + +-PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " +- "defined missing variable to another to provide more meaningful analysis " ++PROGRAM_INFO("Impute Data", ++ // Short description. ++ "This utility provides several imputation strategies for missing data. " ++ "Given a dataset with missing values, this can impute according to several " ++ "strategies, including user-defined values.", ++ // Long description. ++ "This utility takes a dataset and converts a user-defined missing variable " ++ "to another to provide more meaningful analysis." + "\n\n" + "The program does not modify the original file, but instead makes a " + "separate file to save the output data; You can save the output by " +@@ -35,7 +41,10 @@ PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " + "column-wise dataset, and save the result to result.csv, we could run" + "\n\n" + "$ mlpack_preprocess_imputer -i dataset.csv -o result.csv -m NULL -d 0 \n" +- "> -s listwise_deletion"); ++ "> -s listwise_deletion", ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + PARAM_STRING_IN_REQ("input_file", "File containing data.", "i"); + PARAM_STRING_OUT("output_file", "File to save output into.", "o"); +diff --git a/src/mlpack/methods/preprocess/preprocess_split_main.cpp b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +index a66056012..5fafd05c3 100644 +--- a/src/mlpack/methods/preprocess/preprocess_split_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +@@ -15,11 +15,16 @@ + #include + #include + +-PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " +- "and splits them into a training set and a test set. Before the split, the " +- "points in the dataset are randomly reordered. The percentage of the " +- "dataset to be used as the test set can be specified with the " + +- PRINT_PARAM_STRING("test_ratio") + " parameter; the default is 0.2 (20%)." ++PROGRAM_INFO("Split Data", ++ // Short description. ++ "A utility to split data into a training and testing dataset. This can " ++ "also split labels according to the same split.", ++ // Long description. ++ "This utility takes a dataset and optionally labels and splits them into a " ++ "training set and a test set. Before the split, the points in the dataset " ++ "are randomly reordered. The percentage of the dataset to be used as the " ++ "test set can be specified with the " + PRINT_PARAM_STRING("test_ratio") + ++ " parameter; the default is 0.2 (20%)." + "\n\n" + "The output training and test matrices may be saved with the " + + PRINT_PARAM_STRING("training") + " and " + PRINT_PARAM_STRING("test") + +@@ -48,7 +53,10 @@ PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " + "\n\n" + + PRINT_CALL("preprocess_split", "input", "X", "input_labels", "y", + "test_ratio", 0.3, "training", "X_train", "training_labels", "y_train", +- "test", "X_test", "test_labels", "y_test")); ++ "test", "X_test", "test_labels", "y_test"), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data.", "i"); +diff --git a/src/mlpack/methods/radical/CMakeLists.txt b/src/mlpack/methods/radical/CMakeLists.txt +index 886566b11..c7dc29cac 100644 +--- a/src/mlpack/methods/radical/CMakeLists.txt ++++ b/src/mlpack/methods/radical/CMakeLists.txt +@@ -15,3 +15,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(radical) + add_python_binding(radical) ++add_markdown_docs(radical "cli;python" "transformations") +diff --git a/src/mlpack/methods/radical/radical_main.cpp b/src/mlpack/methods/radical/radical_main.cpp +index 7d5e59447..2de949635 100644 +--- a/src/mlpack/methods/radical/radical_main.cpp ++++ b/src/mlpack/methods/radical/radical_main.cpp +@@ -15,11 +15,18 @@ + #include + #include "radical.hpp" + +-PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" +- "component analysis (ICA). Assuming that we have an input matrix X, the" +- "goal is to find a square unmixing matrix W such that Y = W * X and the " +- "dimensions of Y are independent components. If the algorithm is running" +- "particularly slowly, try reducing the number of replicates." ++PROGRAM_INFO("RADICAL", ++ // Short description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Given a dataset, this can decompose the dataset into an unmixing " ++ "matrix and an independent component matrix; this can be useful for " ++ "preprocessing.", ++ // Long description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Assuming that we have an input matrix X, the goal is to find a " ++ "square unmixing matrix W such that Y = W * X and the dimensions of Y are " ++ "independent components. If the algorithm is running particularly slowly, " ++ "try reducing the number of replicates." + "\n\n" + "The input matrix to perform ICA on should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter. The output matrix Y may be " +@@ -31,7 +38,14 @@ PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" + "40 replicates, saving the independent components to " + + PRINT_DATASET("ic") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic")); ++ PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic"), ++ SEE_ALSO("Independent component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Independent_component_analysis"), ++ SEE_ALSO("ICA using spacings estimates of entropy (pdf)", ++ "http://www.jmlr.org/papers/volume4/learned-miller03a/" ++ "learned-miller03a.pdf"), ++ SEE_ALSO("mlpack::radical::Radical C++ class documentation", ++ "@doxygen/classmlpack_1_1radical_1_1Radical.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset for ICA.", "i"); + +diff --git a/src/mlpack/methods/random_forest/CMakeLists.txt b/src/mlpack/methods/random_forest/CMakeLists.txt +index 3ac70de8a..89b03837d 100644 +--- a/src/mlpack/methods/random_forest/CMakeLists.txt ++++ b/src/mlpack/methods/random_forest/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(random_forest) + add_python_binding(random_forest) ++add_markdown_docs(random_forest "cli;python" "classification") +diff --git a/src/mlpack/methods/random_forest/random_forest_main.cpp b/src/mlpack/methods/random_forest/random_forest_main.cpp +index ed22222cf..d0916ed6b 100644 +--- a/src/mlpack/methods/random_forest/random_forest_main.cpp ++++ b/src/mlpack/methods/random_forest/random_forest_main.cpp +@@ -20,12 +20,27 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Random forests", ++ // Short description. ++ "An implementation of the standard random forest algorithm by Leo Breiman " ++ "for classification. Given labeled data, a random forest can be trained " ++ "and saved for future use; or, a pre-trained random forest can be used for " ++ "classification.", ++ // Long description. + "This program is an implementation of the standard random forest " + "classification algorithm by Leo Breiman. A random forest can be " + "trained and saved for later use, or a random forest may be loaded " + "and predictions or class probabilities for points may be generated." + "\n\n" +- "This documentation will be rewritten once #880 is merged."); ++ "This documentation will be rewritten once #880 is merged.", ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@hoeffding_tree", "#hoeffding_tree"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("Random forest on Wikipedia", ++ "https://en.wikipedia.org/wiki/Random_forest"), ++ SEE_ALSO("Random forests (pdf)", ++ "https://link.springer.com/content/pdf/10.1023/A:1010933404324.pdf"), ++ SEE_ALSO("mlpack::tree::RandomForest C++ class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1RandomForest.html")); + + PARAM_MATRIX_IN("training", "Training dataset.", "t"); + PARAM_UROW_IN("labels", "Labels for training dataset.", "l"); +diff --git a/src/mlpack/methods/range_search/CMakeLists.txt b/src/mlpack/methods/range_search/CMakeLists.txt +index 9d29343db..68a020315 100644 +--- a/src/mlpack/methods/range_search/CMakeLists.txt ++++ b/src/mlpack/methods/range_search/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(range_search) + #add_python_binding(range_search) ++add_markdown_docs(range_search "cli" "geometry") +diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp +index 27e49caf1..c50bb709b 100644 +--- a/src/mlpack/methods/range_search/range_search_main.cpp ++++ b/src/mlpack/methods/range_search/range_search_main.cpp +@@ -29,6 +29,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("Range Search", ++ // Short description. ++ "An implementation of range search with single-tree and dual-tree " ++ "algorithms. Given a set of reference points and a set of query points and" ++ " a range, this can find the set of reference points within the desired " ++ "range for each query point, and any trees built during the computation can" ++ " be saved for reuse with future range searches.", ++ // Long description. + "This program implements range search with a Euclidean distance metric. " + "For a given query point, a given range, and a given set of reference " + "points, the program will return all of the reference points with distance " +@@ -55,7 +62,14 @@ PROGRAM_INFO("Range Search", + " resultant CSV-like files may not be loadable by many programs. However, " + "at this time a better way to store this non-square result is not known. " + "As a result, any output files will be written as CSVs in this manner, " +- "regardless of the given extension."); ++ "regardless of the given extension.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Range searching on Wikipedia", ++ "https://en.wikipedia.org/wiki/Range_searching"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::range::RangeSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1range_1_1RangeSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/rann/CMakeLists.txt b/src/mlpack/methods/rann/CMakeLists.txt +index 457a11dd6..3ba78994a 100644 +--- a/src/mlpack/methods/rann/CMakeLists.txt ++++ b/src/mlpack/methods/rann/CMakeLists.txt +@@ -37,3 +37,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # reference sets. + add_cli_executable(krann) + add_python_binding(krann) ++add_markdown_docs(krann "cli;python" "geometry") +diff --git a/src/mlpack/methods/rann/krann_main.cpp b/src/mlpack/methods/rann/krann_main.cpp +index 703fdd085..6a74e5c10 100644 +--- a/src/mlpack/methods/rann/krann_main.cpp ++++ b/src/mlpack/methods/rann/krann_main.cpp +@@ -30,6 +30,13 @@ typedef RAModel RANNModel; + + // Information about the program itself. + PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", ++ // Short description. ++ "An implementation of rank-approximate k-nearest-neighbor search (kRANN) " ++ " using single-tree and dual-tree algorithms. Given a set of reference " ++ "points and query points, this can find the k nearest neighbors in the " ++ "reference set of each query point using trees; trees that are built can " ++ "be saved for future use.", ++ // Long description. + "This program will calculate the k rank-approximate-nearest-neighbors of a " + "set of points. You may specify a separate set of reference points and " + "query points, or just a reference set which will be used as both the " +@@ -53,7 +60,15 @@ PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", + "neighbors output file corresponds to the index of the point in the " + "reference set which is the i'th nearest neighbor from the point in the " + "query set with index j. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("Rank-approximate nearest neighbor search: Retaining meaning and " ++ "speed in high dimensions (pdf)", "https://papers.nips.cc/paper/3864-" ++ "rank-approximate-nearest-neighbor-search-retaining-meaning-and-speed-" ++ "in-high-dimensions.pdf"), ++ SEE_ALSO("mlpack::neighbor::RASearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1RASearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/softmax_regression/CMakeLists.txt b/src/mlpack/methods/softmax_regression/CMakeLists.txt +index c6770f7c4..60a7d4cff 100644 +--- a/src/mlpack/methods/softmax_regression/CMakeLists.txt ++++ b/src/mlpack/methods/softmax_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(softmax_regression) + add_python_binding(softmax_regression) ++add_markdown_docs(softmax_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +index 97b54a2a5..4e3f88e76 100644 +--- a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp ++++ b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +@@ -24,11 +24,18 @@ using namespace mlpack::regression; + using namespace mlpack::util; + + // Define parameters for the executable. +-PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " +- "a generalization of logistic regression to the multiclass case, and has " +- "support for L2 regularization. The program is able to train a model, load" +- " an existing model, and give predictions (and optionally their accuracy) " +- "for test data." ++PROGRAM_INFO("Softmax Regression", ++ // Short description. ++ "An implementation of softmax regression for classification, which is a " ++ "multiclass generalization of logistic regression. Given labeled data, a " ++ "softmax regression model can be trained and saved for future use, or, a " ++ "pre-trained softmax regression model can be used for classification of " ++ "new points.", ++ // Long description. ++ "This program performs softmax regression, a generalization of logistic " ++ "regression to the multiclass case, and has support for L2 regularization. " ++ " The program is able to train a model, load an existing model, and give " ++ "predictions (and optionally their accuracy) for test data." + "\n\n" + "Training a softmax regression model is done by giving a file of training " + "points with the " + PRINT_PARAM_STRING("training") + " parameter and their" +@@ -71,7 +78,14 @@ PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " + " " + PRINT_DATASET("predictions") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("softmax_regression", "input_model", "sr_model", "test", +- "test_points", "predictions", "predictions")); ++ "test_points", "predictions", "predictions"), ++ SEE_ALSO("@logistic_regression", "#logistic_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Multinomial logistic regression (softmax regression) on " ++ "Wikipedia", ++ "https://en.wikipedia.org/wiki/Multinomial_logistic_regression"), ++ SEE_ALSO("mlpack::regression::SoftmaxRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1SoftmaxRegression.html")); + + // Required options. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/sparse_coding/CMakeLists.txt b/src/mlpack/methods/sparse_coding/CMakeLists.txt +index cfd1108cb..7bb0f7b79 100644 +--- a/src/mlpack/methods/sparse_coding/CMakeLists.txt ++++ b/src/mlpack/methods/sparse_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(sparse_coding) + add_python_binding(sparse_coding) ++add_markdown_docs(sparse_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +index e4b561f53..515cb78eb 100644 +--- a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp ++++ b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +@@ -22,12 +22,20 @@ using namespace mlpack::math; + using namespace mlpack::sparse_coding; + using namespace mlpack::util; + +-PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " +- "Dictionary Learning, which achieves sparsity via an l1-norm regularizer on" +- " the codes (LASSO) or an (l1+l2)-norm regularizer on the codes (the " +- "Elastic Net). Given a dense data matrix X with d dimensions and n points," +- " sparse coding seeks to find a dense dictionary matrix D with k atoms in " +- "d dimensions, and a sparse coding matrix Z with n points in k dimensions." ++PROGRAM_INFO("Sparse Coding", ++ // Short description. ++ "An implementation of Sparse Coding with Dictionary Learning. Given a " ++ "dataset, this will decompose the dataset into a sparse combination of a " ++ "few dictionary elements, where the dictionary is learned during " ++ "computation; a dictionary can be reused for future sparse coding of new " ++ "points.", ++ // Long description. ++ "An implementation of Sparse Coding with Dictionary Learning, which " ++ "achieves sparsity via an l1-norm regularizer on the codes (LASSO) or an " ++ "(l1+l2)-norm regularizer on the codes (the Elastic Net). Given a dense " ++ "data matrix X with d dimensions and n points, sparse coding seeks to find " ++ "a dense dictionary matrix D with k atoms in d dimensions, and a sparse " ++ "coding matrix Z with n points in k dimensions." + "\n\n" + "The original data matrix X can then be reconstructed as Z * D. Therefore," + " this program finds a representation of each point in X as a sparse linear" +@@ -62,7 +70,18 @@ PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " + PRINT_DATASET("codes") + ": " + "\n\n" + + PRINT_CALL("sparse_coding", "input_model", "model", "test", "otherdata", +- "codes", "codes")); ++ "codes", "codes"), ++ SEE_ALSO("@local_coordinate_coding", "#local_coordinate_coding"), ++ SEE_ALSO("Sparse dictionary learning on Wikipedia", ++ "https://en.wikipedia.org/wiki/Sparse_dictionary_learning"), ++ SEE_ALSO("Efficient sparse coding algorithms (pdf)", ++ "http://papers.nips.cc/paper/2979-efficient-sparse-coding-" ++ "algorithms.pdf"), ++ SEE_ALSO("Regularization and variable selection via the elastic net", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4696&" ++ "rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::sparse_coding::SparseCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1sparse__coding_1_1SparseCoding.html")); + + // Train the model. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +-- +2.20.1 + diff --git a/_src/markdown-patches/mlpack-3.0.1-markdown.patch b/_src/markdown-patches/mlpack-3.0.1-markdown.patch new file mode 100644 index 000000000..6be08c820 --- /dev/null +++ b/_src/markdown-patches/mlpack-3.0.1-markdown.patch @@ -0,0 +1,7187 @@ +From 14661d363e6bfa12a5661685f7cc597797e2a8dc Mon Sep 17 00:00:00 2001 +From: Ryan Curtin +Date: Wed, 6 Mar 2019 19:56:30 -0500 +Subject: [PATCH] Backport Markdown support onto mlpack 3.0.1. + +--- + CMake/RunProgram.cmake | 8 + + CMakeLists.txt | 14 + + src/mlpack/CMakeLists.txt | 9 +- + src/mlpack/bindings/CMakeLists.txt | 2 + + src/mlpack/bindings/cli/CMakeLists.txt | 2 + + .../bindings/cli/default_param_impl.hpp | 59 +- + src/mlpack/bindings/cli/end_program.hpp | 2 +- + .../bindings/cli/get_printable_type.hpp | 79 +++ + .../bindings/cli/get_printable_type_impl.hpp | 109 ++++ + .../bindings/cli/print_doc_functions.hpp | 38 ++ + .../bindings/cli/print_doc_functions_impl.hpp | 110 +++- + src/mlpack/bindings/cli/print_help.cpp | 6 +- + src/mlpack/bindings/cli/print_type_doc.hpp | 81 +++ + .../bindings/cli/print_type_doc_impl.hpp | 173 ++++++ + src/mlpack/bindings/cli/string_type_param.hpp | 6 - + .../bindings/cli/string_type_param_impl.hpp | 10 - + src/mlpack/bindings/markdown/CMakeLists.txt | 209 +++++++ + .../markdown/MarkdownCategories.cmake | 11 + + src/mlpack/bindings/markdown/binding_info.cpp | 49 ++ + src/mlpack/bindings/markdown/binding_info.hpp | 62 ++ + .../bindings/markdown/default_param.hpp | 45 ++ + .../markdown/generate_markdown.binding.cpp.in | 46 ++ + .../markdown/generate_markdown.binding.hpp.in | 28 + + .../markdown/generate_markdown.cpp.in | 114 ++++ + .../bindings/markdown/get_binding_name.cpp | 40 ++ + .../bindings/markdown/get_binding_name.hpp | 30 + + src/mlpack/bindings/markdown/get_param.hpp | 38 ++ + .../bindings/markdown/get_printable_param.hpp | 126 ++++ + .../markdown/get_printable_param_name.hpp | 83 +++ + .../get_printable_param_name_impl.hpp | 79 +++ + .../markdown/get_printable_param_value.hpp | 88 +++ + .../get_printable_param_value_impl.hpp | 84 +++ + .../bindings/markdown/get_printable_type.hpp | 62 ++ + .../bindings/markdown/is_serializable.hpp | 63 ++ + src/mlpack/bindings/markdown/md_option.hpp | 109 ++++ + .../bindings/markdown/print_doc_functions.hpp | 109 ++++ + .../markdown/print_doc_functions_impl.hpp | 540 ++++++++++++++++++ + src/mlpack/bindings/markdown/print_docs.cpp | 218 +++++++ + src/mlpack/bindings/markdown/print_docs.hpp | 33 ++ + .../bindings/markdown/print_type_doc.hpp | 46 ++ + .../bindings/markdown/program_doc_wrapper.hpp | 41 ++ + .../bindings/markdown/res/change_language.js | 102 ++++ + .../bindings/markdown/res/formatting.css | 142 +++++ + src/mlpack/bindings/markdown/res/menu_bg.png | Bin 0 -> 395 bytes + src/mlpack/bindings/python/CMakeLists.txt | 4 + + src/mlpack/bindings/python/default_param.hpp | 95 +++ + .../bindings/python/default_param_impl.hpp | 147 +++++ + .../bindings/python/get_cython_type.hpp | 10 - + .../bindings/python/get_printable_type.hpp | 120 ++++ + .../python/get_printable_type_impl.hpp | 151 +++++ + src/mlpack/bindings/python/print_doc.hpp | 22 +- + .../bindings/python/print_doc_functions.hpp | 26 + + .../python/print_doc_functions_impl.hpp | 124 +++- + .../python/print_input_processing.hpp | 1 - + src/mlpack/bindings/python/print_type_doc.hpp | 81 +++ + .../bindings/python/print_type_doc_impl.hpp | 162 ++++++ + src/mlpack/bindings/python/py_option.hpp | 4 + + .../python/tests/test_python_binding_main.cpp | 1 + + src/mlpack/core/util/cli.cpp | 3 +- + src/mlpack/core/util/mlpack_main.hpp | 68 ++- + src/mlpack/core/util/param.hpp | 31 +- + src/mlpack/core/util/program_doc.cpp | 30 +- + src/mlpack/core/util/program_doc.hpp | 17 +- + src/mlpack/methods/CMakeLists.txt | 4 + + src/mlpack/methods/adaboost/CMakeLists.txt | 1 + + src/mlpack/methods/adaboost/adaboost_main.cpp | 21 +- + src/mlpack/methods/approx_kfn/CMakeLists.txt | 1 + + .../methods/approx_kfn/approx_kfn_main.cpp | 19 +- + src/mlpack/methods/cf/CMakeLists.txt | 1 + + src/mlpack/methods/cf/cf_main.cpp | 25 +- + src/mlpack/methods/dbscan/CMakeLists.txt | 1 + + src/mlpack/methods/dbscan/dbscan_main.cpp | 12 +- + .../methods/decision_stump/CMakeLists.txt | 1 + + .../decision_stump/decision_stump_main.cpp | 12 +- + .../methods/decision_tree/CMakeLists.txt | 1 + + .../decision_tree/decision_tree_main.cpp | 16 +- + src/mlpack/methods/det/CMakeLists.txt | 1 + + src/mlpack/methods/det/det_main.cpp | 15 +- + src/mlpack/methods/emst/CMakeLists.txt | 1 + + src/mlpack/methods/emst/emst_main.cpp | 13 +- + src/mlpack/methods/fastmks/CMakeLists.txt | 1 + + src/mlpack/methods/fastmks/fastmks_main.cpp | 15 +- + src/mlpack/methods/gmm/CMakeLists.txt | 5 + + src/mlpack/methods/gmm/gmm_generate_main.cpp | 12 +- + .../methods/gmm/gmm_probability_main.cpp | 13 +- + src/mlpack/methods/gmm/gmm_train_main.cpp | 13 +- + src/mlpack/methods/hmm/CMakeLists.txt | 7 + + src/mlpack/methods/hmm/hmm_generate_main.cpp | 18 +- + src/mlpack/methods/hmm/hmm_loglik_main.cpp | 19 +- + src/mlpack/methods/hmm/hmm_train_main.cpp | 21 +- + src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 19 +- + .../methods/hoeffding_trees/CMakeLists.txt | 1 + + .../hoeffding_trees/hoeffding_tree_main.cpp | 14 +- + src/mlpack/methods/kernel_pca/CMakeLists.txt | 1 + + .../methods/kernel_pca/kernel_pca_main.cpp | 17 +- + src/mlpack/methods/kmeans/CMakeLists.txt | 1 + + src/mlpack/methods/kmeans/kmeans_main.cpp | 32 +- + src/mlpack/methods/lars/CMakeLists.txt | 1 + + src/mlpack/methods/lars/lars_main.cpp | 21 +- + .../methods/linear_regression/CMakeLists.txt | 1 + + .../linear_regression_main.cpp | 14 +- + .../local_coordinate_coding/CMakeLists.txt | 1 + + .../local_coordinate_coding_main.cpp | 14 +- + .../logistic_regression/CMakeLists.txt | 1 + + .../logistic_regression_main.cpp | 17 +- + src/mlpack/methods/lsh/CMakeLists.txt | 1 + + src/mlpack/methods/lsh/lsh_main.cpp | 17 +- + src/mlpack/methods/mean_shift/CMakeLists.txt | 1 + + .../methods/mean_shift/mean_shift_main.cpp | 23 +- + src/mlpack/methods/naive_bayes/CMakeLists.txt | 1 + + src/mlpack/methods/naive_bayes/nbc_main.cpp | 14 +- + src/mlpack/methods/nca/CMakeLists.txt | 1 + + src/mlpack/methods/nca/nca_main.cpp | 15 +- + .../methods/neighbor_search/CMakeLists.txt | 3 + + .../methods/neighbor_search/kfn_main.cpp | 14 +- + .../methods/neighbor_search/knn_main.cpp | 17 +- + src/mlpack/methods/nmf/CMakeLists.txt | 1 + + src/mlpack/methods/nmf/nmf_main.cpp | 25 +- + src/mlpack/methods/pca/CMakeLists.txt | 1 + + src/mlpack/methods/pca/pca_main.cpp | 24 +- + src/mlpack/methods/perceptron/CMakeLists.txt | 1 + + .../methods/perceptron/perceptron_main.cpp | 13 +- + src/mlpack/methods/preprocess/CMakeLists.txt | 7 + + .../preprocess/preprocess_binarize_main.cpp | 13 +- + .../preprocess/preprocess_describe_main.cpp | 22 +- + .../preprocess/preprocess_imputer_main.cpp | 15 +- + .../preprocess/preprocess_split_main.cpp | 20 +- + src/mlpack/methods/radical/CMakeLists.txt | 1 + + src/mlpack/methods/radical/radical_main.cpp | 26 +- + .../methods/random_forest/CMakeLists.txt | 1 + + .../random_forest/random_forest_main.cpp | 17 +- + .../methods/range_search/CMakeLists.txt | 1 + + .../range_search/range_search_main.cpp | 16 +- + src/mlpack/methods/rann/CMakeLists.txt | 1 + + src/mlpack/methods/rann/krann_main.cpp | 17 +- + .../methods/softmax_regression/CMakeLists.txt | 1 + + .../softmax_regression_main.cpp | 26 +- + .../methods/sparse_coding/CMakeLists.txt | 1 + + .../sparse_coding/sparse_coding_main.cpp | 33 +- + 139 files changed, 5011 insertions(+), 205 deletions(-) + create mode 100644 CMake/RunProgram.cmake + create mode 100644 src/mlpack/bindings/cli/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/cli/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/CMakeLists.txt + create mode 100644 src/mlpack/bindings/markdown/MarkdownCategories.cmake + create mode 100644 src/mlpack/bindings/markdown/binding_info.cpp + create mode 100644 src/mlpack/bindings/markdown/binding_info.hpp + create mode 100644 src/mlpack/bindings/markdown/default_param.hpp + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.cpp.in + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.cpp + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/markdown/is_serializable.hpp + create mode 100644 src/mlpack/bindings/markdown/md_option.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.cpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.hpp + create mode 100644 src/mlpack/bindings/markdown/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/markdown/program_doc_wrapper.hpp + create mode 100644 src/mlpack/bindings/markdown/res/change_language.js + create mode 100644 src/mlpack/bindings/markdown/res/formatting.css + create mode 100644 src/mlpack/bindings/markdown/res/menu_bg.png + create mode 100644 src/mlpack/bindings/python/default_param.hpp + create mode 100644 src/mlpack/bindings/python/default_param_impl.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc_impl.hpp + +diff --git a/CMake/RunProgram.cmake b/CMake/RunProgram.cmake +new file mode 100644 +index 000000000..6ae385314 +--- /dev/null ++++ b/CMake/RunProgram.cmake +@@ -0,0 +1,8 @@ ++# RunProgram.cmake: a CMake script that actually runs the given program to ++# generate a file, which is output into the given directory. ++# ++# This script depends on the following arguments: ++# ++# PROGRAM: the program to run to. ++# OUTPUT_FILE: the file to store the output in. ++execute_process(COMMAND ${PROGRAM} OUTPUT_FILE ${OUTPUT_FILE}) +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 3293b0567..a12b5f0d2 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -15,6 +15,20 @@ option(BUILD_CLI_EXECUTABLES "Build command-line executables." ON) + option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) + option(BUILD_SHARED_LIBS + "Compile shared libraries (if OFF, static libraries are compiled)." ON) ++ ++# Currently Python bindings aren't known to build successfully on Windows, so ++# set BUILD_PYTHON_BINDINGS to OFF when the platform is Windows. ++if (WIN32) ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." OFF) ++ message(WARNING "By default Python bindings are not compiled for Windows because they are not known to work. Set BUILD_PYTHON_BINDINGS to ON if you want them built.") ++else () ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) ++endif() ++ ++# Build Markdown bindings for documentation. This is used as part of website ++# generation. ++option(BUILD_MARKDOWN_BINDINGS "Build Markdown bindings for website documentation." OFF) ++ + option(BUILD_WITH_COVERAGE + "Build with support for code coverage tools (gcc only)." OFF) + option(MATHJAX +diff --git a/src/mlpack/CMakeLists.txt b/src/mlpack/CMakeLists.txt +index 29c545c9e..b555e0100 100644 +--- a/src/mlpack/CMakeLists.txt ++++ b/src/mlpack/CMakeLists.txt +@@ -14,7 +14,7 @@ set(DIRS + ) + + foreach(dir ${DIRS}) +- add_subdirectory(${dir}) ++ add_subdirectory(${dir}) + endforeach() + + # MLPACK_SRCS is set in the subdirectories. The dependencies (MLPACK_LIBRARIES) +@@ -40,8 +40,7 @@ if (NOT BUILD_SHARED_LIBS) + add_definitions(-DMLPACK_STATIC_DEFINE) + endif () + +-target_link_libraries(mlpack +- ${MLPACK_LIBRARIES}) ++target_link_libraries(mlpack ${MLPACK_LIBRARIES}) + + set_target_properties(mlpack + PROPERTIES +@@ -122,3 +121,7 @@ if (BUILD_PYTHON_BINDINGS) + configure_file(${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/setup.py.in + ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py) + endif () ++ ++# If we are building Markdown documentation, we have to run some setup after we ++# recurse into methods/. If not, this function is empty. ++post_markdown_setup() +diff --git a/src/mlpack/bindings/CMakeLists.txt b/src/mlpack/bindings/CMakeLists.txt +index f35249ddd..e726c515b 100644 +--- a/src/mlpack/bindings/CMakeLists.txt ++++ b/src/mlpack/bindings/CMakeLists.txt +@@ -1,6 +1,7 @@ + # All we have to do is recurse into the subdirectories. + set(DIRS + cli ++ markdown + python + tests + ) +@@ -9,6 +10,7 @@ foreach(dir ${DIRS}) + add_subdirectory(${dir}) + endforeach() + ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) + set(MLPACK_SRCS ${MLPACK_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) + set(DISABLE_CFLAGS ${DISABLE_CFLAGS} PARENT_SCOPE) +diff --git a/src/mlpack/bindings/cli/CMakeLists.txt b/src/mlpack/bindings/cli/CMakeLists.txt +index 83c50292e..280043fa6 100644 +--- a/src/mlpack/bindings/cli/CMakeLists.txt ++++ b/src/mlpack/bindings/cli/CMakeLists.txt +@@ -25,6 +25,8 @@ set(SOURCES + print_doc_functions_impl.hpp + print_help.hpp + print_help.cpp ++ print_type_doc.hpp ++ print_type_doc_impl.hpp + set_param.hpp + string_type_param.hpp + string_type_param_impl.hpp +diff --git a/src/mlpack/bindings/cli/default_param_impl.hpp b/src/mlpack/bindings/cli/default_param_impl.hpp +index 8c1740c1f..8c9ea7896 100644 +--- a/src/mlpack/bindings/cli/default_param_impl.hpp ++++ b/src/mlpack/bindings/cli/default_param_impl.hpp +@@ -32,7 +32,9 @@ std::string DefaultParamImpl( + std::tuple>>::type* /* junk */) + { + std::ostringstream oss; +- oss << boost::any_cast(data.value); ++ if (!std::is_same::value) ++ oss << boost::any_cast(data.value); ++ + return oss.str(); + } + +@@ -48,9 +50,35 @@ std::string DefaultParamImpl( + std::ostringstream oss; + const T& vector = boost::any_cast(data.value); + oss << "["; +- for (size_t i = 0; i < vector.size() - 1; ++i) +- oss << vector[i] << " "; +- oss << vector[vector.size() - 1] << "]"; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ + return oss.str(); + } + +@@ -67,39 +95,30 @@ std::string DefaultParamImpl( + } + + /** +- * Return the default value of a matrix option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a matrix option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::enable_if_c< + arma::is_arma_type::value || + std::is_same>::value>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ // The filename will always be empty. ++ return "''"; + } + + /** +- * Return the default value of a model option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a model option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::disable_if>::type* /* junk */, + const typename boost::enable_if>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ return "''"; + } + + +diff --git a/src/mlpack/bindings/cli/end_program.hpp b/src/mlpack/bindings/cli/end_program.hpp +index 031c49d5f..8e412db10 100644 +--- a/src/mlpack/bindings/cli/end_program.hpp ++++ b/src/mlpack/bindings/cli/end_program.hpp +@@ -50,7 +50,7 @@ inline void EndProgram() + while (it != parameters.end()) + { + // Now, figure out what type it is, and print it. +- // We can handle strings, ints, bools, floats, doubles. ++ // We can handle strings, ints, bools, doubles. + const util::ParamData& data = it->second; + std::string boostName; + CLI::GetSingleton().functionMap[data.tname]["MapParameterName"](data, +diff --git a/src/mlpack/bindings/cli/get_printable_type.hpp b/src/mlpack/bindings/cli/get_printable_type.hpp +new file mode 100644 +index 000000000..c4634c4ad +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/get_printable_type_impl.hpp b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..bb1ff85a5 +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ if (std::is_same::value) ++ return "flag"; ++ else if (std::is_same::value) ++ return "int"; ++ else if (std::is_same::value) ++ return "double"; ++ else if (std::is_same::value) ++ return "string"; ++ else ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ return "int vector"; ++ else if (std::is_same>::value) ++ return "string vector"; ++ else ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ return "2-d matrix file"; ++ else if (std::is_same>::value) ++ return "2-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else ++ throw std::invalid_argument("unknown Armadillo type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "2-d categorical matrix file"; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return data.cppType + " file"; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_doc_functions.hpp b/src/mlpack/bindings/cli/print_doc_functions.hpp +index cfc024b2c..81ce73816 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions.hpp +@@ -20,12 +20,38 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ ++/** ++ * Print documentation for each of the types. ++ */ ++inline std::string PrintTypeDocs(); ++ + /** + * Given a parameter type, print the corresponding value. + */ + template + inline std::string PrintValue(const T& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -36,6 +62,12 @@ inline std::string PrintDataset(const std::string& dataset); + */ + inline std::string PrintModel(const std::string& model); + ++/** ++ * Print the type of a parameter that a user would specify from the ++ * command-line. ++ */ ++inline std::string PrintType(const util::ParamData& param); ++ + /** + * Base case for recursion. + */ +@@ -56,6 +88,12 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +index 0f37e7d5e..96349f5dd 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +@@ -20,6 +20,31 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ return "mlpack_" + bindingName; ++} ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& /* bindingName */) ++{ ++ return ""; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return ""; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -35,6 +60,23 @@ inline std::string PrintValue(const T& value, bool quotes) + return oss.str(); + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -107,10 +149,76 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args) + { +- return util::HyphenateString("$ " + programName + " " + ++ return util::HyphenateString("$ " + GetBindingName(programName) + " " + + ProcessOptions(args...), 2); + } + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << "$ " << GetBindingName(programName); ++ ++ // Handle all options---first input options, then output options. ++ const std::map& parameters = CLI::Parameters(); ++ ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " "; ++ if (!it->second.required) ++ oss << "["; ++ ++ oss << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ ++ if (!it->second.required) ++ oss << "]"; ++ } ++ ++ // Now get the output options. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " [" << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ oss << "]"; ++ } ++ ++ return util::HyphenateString(oss.str(), 8); ++} ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_help.cpp b/src/mlpack/bindings/cli/print_help.cpp +index c6ef6c90d..ff62a7154 100644 +--- a/src/mlpack/bindings/cli/print_help.cpp ++++ b/src/mlpack/bindings/cli/print_help.cpp +@@ -118,7 +118,11 @@ void PrintHelp(const std::string& param) + } + + // Append default value to description. +- if (pass >= 1 && data.cppType != "bool") ++ if (pass >= 1 && (data.cppType == "int" || data.cppType == "double" || ++ data.cppType == "std::string" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector")) + { + std::string defaultValue; + CLI::GetSingleton().functionMap[data.tname]["DefaultParam"](data, +diff --git a/src/mlpack/bindings/cli/print_type_doc.hpp b/src/mlpack/bindings/cli/print_type_doc.hpp +new file mode 100644 +index 000000000..677854e60 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_type_doc_impl.hpp b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..3a5a20912 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +@@ -0,0 +1,173 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option. If not specified, it is false; if " ++ "specified, it is true."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A vector of integers, separated by commas (i.e., \"1,2,3\")."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A vector of strings, separated by commas (i.e., " ++ "\"hello\",\"goodbye\")."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ return "A data matrix filename. The file can be CSV (.csv), TSV (.csv), " ++ "ASCII (space-separated values, .txt), Armadillo ASCII (.txt), PGM " ++ "(.pgm), PPM (.ppm), Armadillo binary (.bin), or HDF5 (.h5, .hdf, " ++ ".hdf5, or .he5), if mlpack was compiled with HDF5 support. The type " ++ "of the data is detected by the extension of the filename. The storage" ++ " should be such that one row corresponds to one point, and one column " ++ "corresponds to one dimension (this is the typical storage format for " ++ "on-disk data). All values of the matrix will be loaded as double-" ++ "precision floating point data."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A data matrix filename, where the matrix holds only non-negative " ++ "integer values. This type is often used for labels or indices. The " ++ "file can be CSV (.csv), TSV (.csv), ASCII (space-separated values, " ++ ".txt), Armadillo ASCII (.txt), PGM (.pgm), PPM (.ppm), Armadillo " ++ "binary (.bin), or HDF5 (.h5, .hdf, .hdf5, or .he5), if mlpack was " ++ "compiled with HDF5 support. The type of the data is detected by the " ++ "extension of the filename. The storage should be such that one row " ++ "corresponds to one point, and one column corresponds to one dimension " ++ "(this is the typical storage format for on-disk data). All values of " ++ "the matrix will be loaded as unsigned integers."; ++ } ++ else if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "A one-dimensional vector filename. This file can take the same " ++ "formats as the data matrix filenames; however, it must either contain " ++ "one row and many columns, or one column and many rows."; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "A one-dimensional vector filename, where the matrix holds only non-" ++ "negative integer values. This type is typically used for labels or " ++ "predictions or other indices. This file can take the same formats as " ++ "the data matrix filenames; however, it must either contain one row and" ++ " many columns, or one column and many rows."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A filename for a data matrix that can contain categorical " ++ "(non-numeric) data. If the file contains only numeric data, then the " ++ "same formats for regular data matrices can be used. If the file " ++ "contains strings or other values that can't be parsed as numbers, then " ++ "the type to be loaded must be CSV (.csv) or ARFF (.arff). Any non-" ++ "numeric data will be converted to an unsigned integer value, and " ++ "dimensions where the data is converted will be treated as categorical " ++ "dimensions. When using this format, there is no need for one-hot " ++ "encoding of categorical data."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "A filename containing an mlpack model. These can have one of three " ++ "formats: binary (.bin), text (.txt), and XML (.xml). The XML format " ++ "produces the largest (but most human-readable) files, while the binary " ++ "format can be significantly more compact and quicker to load and save."; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/string_type_param.hpp b/src/mlpack/bindings/cli/string_type_param.hpp +index 28caa8991..702fb4668 100644 +--- a/src/mlpack/bindings/cli/string_type_param.hpp ++++ b/src/mlpack/bindings/cli/string_type_param.hpp +@@ -74,12 +74,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + const void* /* input */, + void* output); + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output); +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/cli/string_type_param_impl.hpp b/src/mlpack/bindings/cli/string_type_param_impl.hpp +index 01065086e..86fb96302 100644 +--- a/src/mlpack/bindings/cli/string_type_param_impl.hpp ++++ b/src/mlpack/bindings/cli/string_type_param_impl.hpp +@@ -80,16 +80,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + *outstr = "string"; + } + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output) +-{ +- std::string* outstr = (std::string*) output; +- *outstr = "float"; +-} +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/markdown/CMakeLists.txt b/src/mlpack/bindings/markdown/CMakeLists.txt +new file mode 100644 +index 000000000..b6b31cfba +--- /dev/null ++++ b/src/mlpack/bindings/markdown/CMakeLists.txt +@@ -0,0 +1,209 @@ ++macro (not_found_return message) ++ message(STATUS "${message}") ++ ++ macro (add_markdown_docs name languages category) ++ # Do nothing. ++ endmacro() ++ ++ function (post_markdown_setup) ++ # Do nothing. ++ endfunction () ++ ++ return () ++endmacro () ++ ++if (NOT BUILD_MARKDOWN_BINDINGS) ++ not_found_return("Not building Markdown bindings.") ++endif () ++ ++# We don't need to find any libraries or anything to generate markdown ++# documentation. ++ ++# Get categories. The list of allowable categories for add_markdown_docs() is ++# in that file. ++include(MarkdownCategories.cmake) ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) ++ ++# Add sources for Markdown bindings. ++set(SOURCES ++ "binding_info.hpp" ++ "binding_info.cpp" ++ "default_param.hpp" ++ "get_binding_name.hpp" ++ "get_binding_name.cpp" ++ "get_param.hpp" ++ "get_printable_param.hpp" ++ "get_printable_param_name.hpp" ++ "get_printable_param_name_impl.hpp" ++ "get_printable_type.hpp" ++ "md_option.hpp" ++ "print_doc_functions.hpp" ++ "print_doc_functions_impl.hpp" ++ "print_docs.hpp" ++ "print_docs.cpp" ++ "print_type_doc.hpp" ++ "program_doc_wrapper.hpp" ++) ++ ++# Copy all Markdown sources to the build directory. ++add_custom_target(markdown_copy) ++add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++foreach(file ${SOURCES}) ++ add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} ARGS -E copy ++ ${CMAKE_CURRENT_SOURCE_DIR}/${file} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++endforeach() ++ ++# Create the add_markdown_docs() macro. It's meant to be used as ++# 'add_markdown_docs(knn, "cli;python;julia", "classification")', for instance. ++# See the file 'MarkdownCategories.cmake' for valid categories that can be used ++# by the macro. ++macro (add_markdown_docs name languages category) ++ ++ # First, make sure that the category is a valid category. ++ list(FIND MARKDOWN_CATEGORIES ${category} cat_index) ++ if (${cat_index} EQUAL -1) ++ string(CONCAT error_str "add_markdown_docs(): unknown category ${category}!" ++ " See the categories in " ++ "src/mlpack/bindings/markdown/MarkdownCategories.cmake.") ++ message(FATAL_ERROR "${ERROR_STR}") ++ endif () ++ ++ # Next, we should use configure_file() to generate each ++ # generate_markdown..cpp. We need to loop over all the languages for ++ # this binding to do that. ++ set(BINDING ${name}) ++ set(LANGUAGES_PUSH_BACK_CODE "") ++ set(PROGRAM_MAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${name}_main.cpp") ++ foreach (lang ${languages}) ++ set(LANGUAGES_PUSH_BACK_CODE ++ "${LANGUAGES_PUSH_BACK_CODE}\n languages.push_back(\"${lang}\");") ++ set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} ${lang}) ++ endforeach () ++ list(REMOVE_DUPLICATES MARKDOWN_ALL_LANGUAGES_LIST) ++ ++ # Do the actual file configuration. ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.hpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.hpp) ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.cpp) ++ ++ # Lastly, that generate_markdown..cpp should be added to the list of ++ # files to be compiled for the 'generate_markdown' target. We also need to ++ # add information about this binding to a set of variables we have to track. ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.hpp ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.cpp) ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} ${name}) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} ${category}) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++ set (MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) ++endmacro () ++ ++# After all the methods/ directories have been traversed, we can add the ++# 'generate_markdown' target. This function is run at the bottom of ++# methods/CMakeLists.txt. ++function (post_markdown_setup) ++ # We need to generate the program file. This consists of generating three ++ # things: ++ # ++ # - MARKDOWN_INCLUDES: the list of files to be included. ++ # - MARKDOWN_HEADER_CODE: the code used to print the header/sidebar. ++ # - MARKDOWN_CALL_CODE: the code to actually call the functions to print ++ # documentation for each binding. ++ ++ # Iterate over categories of binding. ++ list(LENGTH MARKDOWN_NAMES NUM_MARKDOWN_BINDINGS) ++ list(LENGTH MARKDOWN_CATEGORIES NUM_MARKDOWN_CATEGORIES) ++ math(EXPR cat_limit "${NUM_MARKDOWN_CATEGORIES} - 1") ++ foreach (i RANGE ${cat_limit}) ++ list (GET MARKDOWN_CATEGORIES ${i} cat) ++ # Put the things in this category in a div. ++ string(CONCAT header_code "${MARKDOWN_HEADER_CODE} cout << " ++ "\"
    \" << endl;\n " ++ "cout << \"
    ${cat}:
    \" << endl;\n") ++ set(MARKDOWN_HEADER_CODE ${header_code}) ++ ++ # Add an option for this binding. ++ math(EXPR range_limit "${NUM_MARKDOWN_BINDINGS} - 1") ++ foreach (j RANGE ${range_limit}) ++ list (GET MARKDOWN_NAME_CATEGORIES ${j} category) ++ if (NOT category STREQUAL cat) ++ continue () ++ endif () ++ ++ list (GET MARKDOWN_NAMES ${j} name) ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n Print${name}Headers();") ++ endforeach () ++ ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n cout << \"
    \" << endl << endl;") ++ endforeach () ++ ++ foreach (name ${MARKDOWN_NAMES}) ++ set (MARKDOWN_INCLUDE_CODE ++ "${MARKDOWN_INCLUDE_CODE}\n#include \"generate_markdown.${name}.hpp\"") ++ set (MARKDOWN_CALL_CODE "${MARKDOWN_CALL_CODE}\n Print${name}Docs();") ++ endforeach () ++ ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.cpp) ++ ++ # Remember that this is being run from some other directory, so we have to be ++ # explicit with the locations of the files we are compiling against. ++ add_executable(generate_markdown ++ "${BINDING_BINARY_DIR}/generate_markdown.cpp" ++ ${MARKDOWN_SRCS} ++ "${BINDING_SOURCE_DIR}/binding_info.hpp" ++ "${BINDING_SOURCE_DIR}/binding_info.cpp" ++ "${BINDING_SOURCE_DIR}/default_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.cpp" ++ "${BINDING_SOURCE_DIR}/get_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name_impl.hpp" ++ "${BINDING_SOURCE_DIR}/md_option.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions_impl.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.cpp" ++ "${BINDING_SOURCE_DIR}/program_doc_wrapper.hpp" ++ "${BINDING_SOURCE_DIR}/generate_markdown.cpp") ++ target_link_libraries(generate_markdown mlpack ${MLPACK_LIBRARIES}) ++ add_dependencies(generate_markdown markdown_copy) ++ set_target_properties(generate_markdown PROPERTIES ++ COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_MARKDOWN ++ RUNTIME_OUTPUT_DIRECTORY ${BINDING_BINARY_DIR}) ++ ++ add_custom_target(markdown ALL ++ ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/doc/ ++ COMMAND ${CMAKE_COMMAND} ++ -DPROGRAM=${BINDING_BINARY_DIR}/generate_markdown ++ -DOUTPUT_FILE=${CMAKE_BINARY_DIR}/doc/mlpack.md ++ -P ${CMAKE_SOURCE_DIR}/CMake/RunProgram.cmake ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/change_language.js ++ ${CMAKE_BINARY_DIR}/doc/res/change_languages.js ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/menu_bg.png ++ ${CMAKE_BINARY_DIR}/doc/res/menu_bg.png ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/formatting.css ++ ${CMAKE_BINARY_DIR}/doc/res/formatting.css ++ DEPENDS generate_markdown ++ COMMENT "Generating Markdown documentation for mlpack bindings...") ++endfunction () +diff --git a/src/mlpack/bindings/markdown/MarkdownCategories.cmake b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +new file mode 100644 +index 000000000..6b8d697cf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +@@ -0,0 +1,11 @@ ++# This is a list of categories of binding that Markdown can handle. If the ++# category you choose for a Markdown binding is not in this list, an error will ++# be thrown. ++set (MARKDOWN_CATEGORIES ++ "classification" ++ "regression" ++ "clustering" ++ "geometry" ++ "preprocessing" ++ "misc. / other" ++ "transformations") +diff --git a/src/mlpack/bindings/markdown/binding_info.cpp b/src/mlpack/bindings/markdown/binding_info.cpp +new file mode 100644 +index 000000000..0a61103d3 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.cpp +@@ -0,0 +1,49 @@ ++/** ++ * @file binding_info.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of BindingInfo functions. ++ */ ++#include "binding_info.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++util::ProgramDoc& BindingInfo::GetProgramDoc(const std::string& bindingName) ++{ ++ if (GetSingleton().map.count(bindingName) == 0) ++ { ++ throw std::invalid_argument("Binding name '" + bindingName + ++ "' not known!"); ++ } ++ ++ return GetSingleton().map.at(bindingName); ++} ++ ++/** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++void BindingInfo::RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc) ++{ ++ GetSingleton().map[bindingName] = programDoc; ++} ++ ++//! Get or modify the current language (don't set it to something invalid!). ++std::string& BindingInfo::Language() ++{ ++ return GetSingleton().language; ++} ++ ++BindingInfo& BindingInfo::GetSingleton() ++{ ++ static BindingInfo instance; ++ return instance; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/binding_info.hpp b/src/mlpack/bindings/markdown/binding_info.hpp +new file mode 100644 +index 000000000..6a99d2ebc +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file binding_name.hpp ++ * @author Ryan Curtin ++ * ++ * This file defines the BindingInfo singleton class that is used specifically ++ * for the Markdown bindings to map from a binding name (i.e. "knn") to ++ * multiple ProgramDoc objects, which are then used to generate the ++ * documentation. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The BindingInfo class is used by the Markdown documentation generator to ++ * store multiple ProgramDoc objects, indexed by both the binding name (i.e. ++ * "knn") and the language (i.e. "cli"). ++ */ ++class BindingInfo ++{ ++ public: ++ /** ++ * Return a ProgramDoc object for a given bindingName. ++ */ ++ static util::ProgramDoc& GetProgramDoc(const std::string& bindingName); ++ ++ /** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++ static void RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc); ++ ++ //! Get or modify the current language (don't set it to something invalid!). ++ static std::string& Language(); ++ ++ private: ++ //! Private constructor, so that only one instance can be created. ++ BindingInfo() { } ++ ++ //! Get the singleton. ++ static BindingInfo& GetSingleton(); ++ ++ //! Internally-held map for mapping a binding name to a ProgramDoc name. ++ std::unordered_map map; ++ ++ //! Holds the name of the language that we are currently printing. This is ++ //! modified before printing the documentation, and then used by ++ //! print_doc_functions.hpp during printing to print the correct language. ++ std::string language; ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/default_param.hpp b/src/mlpack/bindings/markdown/default_param.hpp +new file mode 100644 +index 000000000..78af807e0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/default_param.hpp +@@ -0,0 +1,45 @@ ++/** ++ * @file default_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get the default value of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the default value of a parameter into the output string. The type ++ * printed depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::DefaultParamImpl::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::DefaultParamImpl::type>(data); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +new file mode 100644 +index 000000000..18a07e5ec +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +@@ -0,0 +1,46 @@ ++/** ++ * @file generate_markdown.binding.cpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#define BINDING_NAME "${BINDING}" ++ ++#include ++#include "generate_markdown.${BINDING}.hpp" ++#include "binding_info.hpp" ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++static const std::string testName = "${BINDING}"; ++#include <${PROGRAM_MAIN_FILE}> ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintHeaders("${BINDING}", languages); ++} ++ ++void Print${BINDING}Docs() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintDocs("${BINDING}", languages); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +new file mode 100644 +index 000000000..3e81c9c3a +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +@@ -0,0 +1,28 @@ ++/** ++ * @file generate_markdown.binding.hpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++ ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers(); ++void Print${BINDING}Docs(); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +new file mode 100644 +index 000000000..abacae377 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +@@ -0,0 +1,114 @@ ++/** ++ * @file generate_markdown.cpp.in ++ * @author Ryan Curtin ++ * ++ * This file is configured by CMake to generate all of the Markdown required by ++ * the project. ++ */ ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++${MARKDOWN_INCLUDE_CODE} ++ ++using namespace mlpack; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++using namespace std; ++ ++int main() ++{ ++ // These come to use from CMake separated by semicolons. ++ string languageList = "${MARKDOWN_ALL_LANGUAGES_LIST}"; ++ vector languages; ++ size_t index; ++ while ((index = languageList.find(';')) != string::npos) ++ { ++ languages.push_back(languageList.substr(0, index)); ++ languageList = languageList.substr(index + 1); ++ } ++ languages.push_back(languageList); ++ ++ cout << "
    " << endl; ++ ++ // We need to create the input form selector for the language. ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << " " << endl; ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // The links to the data type sections get put here. ++ cout << " - [mlpack overview](#mlpack-overview){: .language-link #always }" ++ << endl; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << " - [data formats](#" << languages[i] << "_data-formats){: " ++ << ".language-link #" << languages[i] << " }" << endl; ++ } ++ ++ ${MARKDOWN_HEADER_CODE} ++ ++ cout << endl << "
    " << endl << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // Create all the headers for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "
    " << endl; ++ cout << "# " << util::GetVersion() << " " << PrintLanguage(languages[i]) ++ << " binding documentation" << endl; ++ cout << "
    " << endl; ++ } ++ ++ /** ++ * "mlpack overview" section. This will go at the top of the page. ++ */ ++ cout << "## mlpack overview" << endl; ++ cout << endl; ++ cout << "mlpack is an intuitive, fast, and flexible C++ machine learning " ++ "library with bindings to other languages. It is meant to be a machine " ++ "learning analog to LAPACK, and aims to implement a wide array of machine" ++ " learning methods and functions as a \"swiss army knife\" for machine " ++ "learning researchers."; ++ cout << endl << endl; ++ cout << "This reference page details mlpack's bindings to other languages. " ++ "Further useful mlpack documentation links are given below."; ++ cout << endl << endl; ++ cout << " - [mlpack homepage](https://www.mlpack.org/)" << endl; ++ cout << " - [mlpack on Github](https://github.com/mlpack/mlpack)" << endl; ++ cout << " - [mlpack main documentation page]" ++ << "(https://www.mlpack.org/docs.html)" << endl; ++ cout << endl; ++ ++ /** ++ * Discussion of different data types section. This goes just below the ++ * overview section at the top of the page. ++ */ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ cout << PrintTypeDocs() << endl; ++ } ++ ++ ${MARKDOWN_CALL_CODE} ++ cout << "
    " << endl << endl; ++ ++ // Make sure script gets included for changeLanguage(). ++ cout << "" << endl; ++} +diff --git a/src/mlpack/bindings/markdown/get_binding_name.cpp b/src/mlpack/bindings/markdown/get_binding_name.cpp +new file mode 100644 +index 000000000..67d229b6c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.cpp +@@ -0,0 +1,40 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#include "get_binding_name.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++std::string GetBindingName(const std::string& language, ++ const std::string& name) ++{ ++ // Unfortunately, every time a new binding is added, this code will need to be ++ // modified. ++ if (language == "cli") ++ { ++ // For command-line programs, all bindings have 'mlpack_' prepended to the ++ // name. ++ return "mlpack_" + name; ++ } ++ else if (language == "python") ++ { ++ // For Python bindings, the name is unchanged. ++ return name; ++ } ++ else ++ { ++ throw std::invalid_argument("Don't know how to compute binding name for " ++ "language \"" + language + "\"! Is the language specified in " ++ "src/mlpack/bindings/markdown/get_binding_name.cpp?"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/get_binding_name.hpp b/src/mlpack/bindings/markdown/get_binding_name.hpp +new file mode 100644 +index 000000000..21c55f05c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.hpp +@@ -0,0 +1,30 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given a language name and a binding name, return the name of that binding for ++ * that language. Note that if a new language is added to the mlpack bindings, ++ * this method will need to be updated so that documentation can be successfully ++ * generated for that language. ++ */ ++std::string GetBindingName(const std::string& language, ++ const std::string& name); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_param.hpp b/src/mlpack/bindings/markdown/get_param.hpp +new file mode 100644 +index 000000000..6c1611f49 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_param.hpp +@@ -0,0 +1,38 @@ ++/** ++ * @file get_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a parameter for a Markdown binding. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * All Markdown binding types are exactly what is held in the ParamData, so no ++ * special handling is necessary. ++ */ ++template ++void GetParam(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ util::ParamData& dmod = const_cast(d); ++ *((T**) output) = boost::any_cast(&dmod.value); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param.hpp b/src/mlpack/bindings/markdown/get_printable_param.hpp +new file mode 100644 +index 000000000..53a862b61 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param.hpp +@@ -0,0 +1,126 @@ ++/** ++ * @file get_printable_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a printable version of parameters. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print an option of a simple type. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a vector option, with spaces between it. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ const T& t = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ for (size_t i = 0; i < t.size(); ++i) ++ oss << t[i] << " "; ++ return oss.str(); ++} ++ ++/** ++ * Print a matrix option (this prints its size). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ // Get the matrix. ++ const T& matrix = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix"; ++ return oss.str(); ++} ++ ++/** ++ * Print a serializable class option (this prints the class name). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << data.cppType << " model at " << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a combination DatasetInfo/matrix parameter. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0) ++{ ++ // Get the matrix. ++ const T& tuple = boost::any_cast(data.value); ++ const arma::mat& matrix = std::get<1>(tuple); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix with dimension type " ++ << "information"; ++ return oss.str(); ++} ++ ++/** ++ * Print an option into a std::string. This should print a short, one-line ++ * representation of the object. The string will be stored in the output ++ * pointer. ++ * ++ * @param data Parameter data struct. ++ * @param input Unused parameter. ++ * @param output Output storage for the string. ++ */ ++template ++void GetPrintableParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParam::type>(data); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name.hpp b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +new file mode 100644 +index 000000000..9ceca6b4c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +@@ -0,0 +1,83 @@ ++/** ++ * @file get_printable_param_name.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamName( ++ const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamName::type>(d); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_name_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +new file mode 100644 +index 000000000..41b9c2972 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_param_name_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "--" + data.name; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value.hpp b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +new file mode 100644 +index 000000000..d58bc93bf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +@@ -0,0 +1,88 @@ ++/** ++ * @file get_printable_param_value.hpp ++ * @author Ryan Curtin ++ * ++ * Given a parameter value, print what the user might actually specify on the ++ * command line. Basically this adds ".csv" to types where data must be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamValue( ++ const util::ParamData& d, ++ const void* input, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamValue::type>(d, ++ *((std::string*) input)); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_value_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +new file mode 100644 +index 000000000..9fce0559b +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +@@ -0,0 +1,84 @@ ++/** ++ * @file get_printable_param_value_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter value that the user would specify on the command line ++ * depending on the type of the option. Basically this adds ".csv" to types ++ * that need to be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return input; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".csv"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".bin"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>>::type*) ++{ ++ return input + ".arff"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_type.hpp b/src/mlpack/bindings/markdown/get_printable_type.hpp +new file mode 100644 +index 000000000..2b292b937 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_type.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::GetPrintableType::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::GetPrintableType::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("GetPrintableType(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the type of a parameter. The type printed depends on the current ++ * setting of BindingInfo::Language(). ++ */ ++template ++std::string GetPrintableType(const util::ParamData& data) ++{ ++ std::string output; ++ GetPrintableType(data, (void*) NULL, (void*) &output); ++ return output; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/is_serializable.hpp b/src/mlpack/bindings/markdown/is_serializable.hpp +new file mode 100644 +index 000000000..b129487b9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/is_serializable.hpp +@@ -0,0 +1,63 @@ ++/** ++ * @file is_serializable.hpp ++ * @author Ryan Curtin ++ * ++ * Return a bool noting whether or not a parameter is serializable. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Return false, because the type is not serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::disable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return false, because even though the type is serializable, it is an ++ * Armadillo type not an mlpack model. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return true, because the type is serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0) ++{ ++ return true; ++} ++ ++/** ++ * Return whether or not the type is serializable. ++ */ ++template ++void IsSerializable(const util::ParamData& /* data */, ++ const void* /* input */, ++ void* output) ++{ ++ *((bool*) output) = IsSerializable::type>(); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/md_option.hpp b/src/mlpack/bindings/markdown/md_option.hpp +new file mode 100644 +index 000000000..dc728c4e9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/md_option.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file md_option.hpp ++ * @author Ryan Curtin ++ * ++ * The Markdown option type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++#define MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++ ++#include ++#include ++#include "default_param.hpp" ++#include "get_param.hpp" ++#include "get_printable_param.hpp" ++#include "get_printable_param_name.hpp" // For cli bindings. ++#include "get_printable_param_value.hpp" // For cli bindings. ++#include "get_printable_type.hpp" ++#include "is_serializable.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The Markdown option class. ++ */ ++template ++class MDOption ++{ ++ public: ++ /** ++ * Construct an MDOption object. When constructed, it will register itself ++ * with CLI. The testName parameter is not used and added for compatibility ++ * reasons. ++ */ ++ MDOption(const T defaultValue, ++ const std::string& identifier, ++ const std::string& description, ++ const std::string& alias, ++ const std::string& cppName, ++ const bool required = false, ++ const bool input = true, ++ const bool noTranspose = false, ++ const std::string& bindingName = "") ++ { ++ // Create the ParamData object to give to CLI. ++ util::ParamData data; ++ ++ data.desc = description; ++ data.name = identifier; ++ data.tname = TYPENAME(T); ++ data.alias = alias[0]; ++ data.wasPassed = false; ++ data.noTranspose = noTranspose; ++ data.required = required; ++ data.input = input; ++ data.loaded = false; ++ // Several options from Python and CLI bindings are persistent. ++ if (identifier == "verbose" || identifier == "copy_all_inputs" || ++ identifier == "help" || identifier == "info" || identifier == "version") ++ data.persistent = true; ++ else ++ data.persistent = false; ++ data.cppType = cppName; ++ ++ // Every parameter we'll get from Markdown will have the correct type. ++ data.value = boost::any(defaultValue); ++ ++ // Restore the parameters for this program. ++ if (identifier != "verbose" && identifier != "copy_all_inputs") ++ CLI::RestoreSettings(bindingName, false); ++ ++ // Set the function pointers that we'll need. Most of these simply delegate ++ // to the current binding type's implementation. Any new language will need ++ // to have all of these implemented, and the Markdown implementation will ++ // need to properly delegate. ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetParam"] = &GetParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = ++ &GetPrintableParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamName"] = ++ &GetPrintableParamName; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamValue"] = ++ &GetPrintableParamValue; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableType"] = ++ &GetPrintableType; ++ CLI::GetSingleton().functionMap[data.tname]["IsSerializable"] = ++ &IsSerializable; ++ ++ // Add the option. ++ CLI::Add(std::move(data)); ++ if (identifier != "verbose" && identifier != "copy_all_inputs" && ++ identifier != "help" && identifier != "info" && identifier != "version") ++ CLI::StoreSettings(bindingName); ++ CLI::ClearSettings(); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions.hpp b/src/mlpack/bindings/markdown/print_doc_functions.hpp +new file mode 100644 +index 000000000..2430fa512 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file print_doc_functions.hpp ++ * @author Ryan Curtin ++ * ++ * This file wraps the different printing functionality of different binding ++ * types. If a new binding type is added, this code will need to be modified so ++ * that Markdown can be printed. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language); ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(const std::string& language); ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs(); ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes); ++ ++/** ++ * Print the default value of an option, unless it is required (in which case ++ * Markdown italicized '--' is printed). ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset); ++ ++/** ++ * Print a model type parameter (add .bin and return). ++ */ ++inline std::string PrintModel(const std::string& model); ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args); ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName); ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d); ++ ++/** ++ * Return whether or not a runtime check on parameters should be ignored. ++ */ ++template ++inline bool IgnoreCheck(const T& t); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "print_doc_functions_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +new file mode 100644 +index 000000000..13359441d +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +@@ -0,0 +1,540 @@ ++/** ++ * @file print_doc_functions_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Call out to different printing functionality for different binding languages. ++ * If a new binding is added, this code must be modified. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++ ++#include "print_doc_functions.hpp" ++#include "binding_info.hpp" ++#include "print_type_doc.hpp" ++#include "get_printable_type.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::GetBindingName(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::GetBindingName(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language) ++{ ++ if (language == "cli") ++ { ++ return "CLI"; ++ } ++ else if (language == "python") ++ { ++ return "Python"; ++ } ++ else ++ { ++ throw std::invalid_argument("PrintLanguage(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintImport(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintImport(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintImport(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintOutputOptionInfo(); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintOutputOptionInfo(); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintOutputOptionInfo(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++namespace priv { ++ ++// We'll need a fake class for printing model type documentation. ++class mlpackModel ++{ ++ public: ++ // Fake serialization to make SFINAE work right for this type. ++ template ++ void serialize(Archive&, const unsigned int) {} ++}; ++ ++} // namespace priv ++ ++// Utility function that returns the first word (as delimited by spaces) of a ++// string. ++inline std::string ToUnderscores(const std::string& str) ++{ ++ std::string ret(str); ++ std::replace(ret.begin(), ret.end(), ' ', '_'); ++ return ret; ++} ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs() ++{ ++ std::ostringstream oss; ++ oss << "
    " << std::endl; ++ oss << "## data formats" << std::endl; ++ oss << "{: .language-types-h2 #" << BindingInfo::Language() ++ << "_data-formats }" << std::endl; ++ oss << std::endl; ++ ++ // Iterate through each of the types that we care about. ++ oss << "mlpack bindings for " << PrintLanguage(BindingInfo::Language()) ++ << " take and return a restricted set of types, for simplicity. These " ++ << "include primitive types, matrix/vector types, categorical matrix " ++ << "types, and model types. Each type is detailed below." << std::endl; ++ oss << std::endl; ++ ++ // Create fake ParamData to pass around. ++ util::ParamData data; ++ data.desc = "fake"; ++ data.name = "fake"; ++ data.tname = std::string(typeid(int).name()); ++ data.cppType = "int"; ++ data.alias = 'f'; ++ data.wasPassed = false; ++ data.noTranspose = true; ++ data.required = false; ++ data.input = true; ++ data.loaded = false; ++ data.persistent = false; ++ data.value = boost::any(int(0)); ++ ++ std::string type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(double).name()); ++ data.cppType = "double"; ++ data.value = boost::any(double(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(bool).name()); ++ data.cppType = "double"; ++ data.value = boost::any(bool(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(std::string).name()); ++ data.cppType = "std::string"; ++ data.value = boost::any(std::string("")); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: " << "#doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ ++ data.tname = std::string(typeid(arma::mat).name()); ++ data.cppType = "arma::mat"; ++ data.value = boost::any(arma::mat()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Mat).name()); ++ data.cppType = "arma::Mat"; ++ data.value = boost::any(arma::Mat()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::rowvec).name()); ++ data.cppType = "arma::rowvec"; ++ data.value = boost::any(arma::rowvec()); ++ const std::string& rowType = GetPrintableType(data); ++ ++ oss << " - `" << rowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(rowType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Row).name()); ++ data.cppType = "arma::Row"; ++ data.value = boost::any(arma::Row()); ++ const std::string& urowType = GetPrintableType>(data); ++ ++ oss << " - `" << urowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(urowType) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::vec).name()); ++ data.cppType = "arma::vec"; ++ data.value = boost::any(arma::vec()); ++ const std::string& colType = GetPrintableType(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (colType != rowType) ++ { ++ oss << " - `" << colType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(colType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ } ++ ++ data.tname = std::string(typeid(arma::Col).name()); ++ data.cppType = "arma::Col"; ++ data.value = boost::any(arma::Col()); ++ const std::string& ucolType = GetPrintableType>(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (ucolType != urowType) ++ { ++ oss << " - `" << ucolType << "`{ #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(ucolType) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ } ++ ++ data.tname = ++ std::string(typeid(std::tuple).name()); ++ data.cppType = "std::tuple"; ++ data.value = boost::any(std::tuple()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(priv::mlpackModel).name()); ++ data.cppType = "mlpackModel"; ++ data.value = boost::any(new priv::mlpackModel()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() ++ << "_model }: " << PrintTypeDoc(data) << std::endl; ++ ++ // Clean up memory. ++ delete boost::any_cast(data.value); ++ ++ oss << std::endl << "
    " << std::endl; ++ ++ return oss.str(); ++} ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintValue(value, quotes); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintValue(value, quotes); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter" + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::ostringstream oss; ++ ++ if (d.required) ++ { ++ oss << "**--**"; ++ } ++ else ++ { ++ if (BindingInfo::Language() == "cli") ++ { ++ oss << cli::PrintDefault(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ oss << python::PrintDefault(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDefault: unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ } ++ ++ return oss.str(); ++} ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintDataset(dataset); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintDataset(dataset); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDataset(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Print a model type parameter. ++ */ ++inline std::string PrintModel(const std::string& model) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintModel(model); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintModel(model); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintModel(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ s += cli::ProgramCall(programName, args...); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ s += python::ProgramCall(programName, args...); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```"; ++ return s; ++} ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += "$ " + import + "\n"; ++ s += cli::ProgramCall(programName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += ">>> " + import + "\n"; ++ s += python::ProgramCall(programName); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```\n"; ++ return s; ++} ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName) ++{ ++ // These functions always put a '' around the parameter, so we will skip that ++ // bit. ++ std::string s; ++ if (BindingInfo::Language() == "cli") ++ { ++ // The CLI bindings put a '' around the parameter, so skip that... ++ s = cli::ParamString(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s = python::ParamString(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("ParamString(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + s.substr(1, s.size() - 2) + "`"; ++} ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d) ++{ ++ std::string output; ++ CLI::GetSingleton().functionMap[d.tname]["GetPrintableType"](d, NULL, ++ &output); ++ // We want to make this a link to the type documentation. ++ std::string anchorType = output; ++ bool result; ++ CLI::GetSingleton().functionMap[d.tname]["IsSerializable"](d, NULL, &result); ++ if (result) ++ anchorType = "model"; ++ ++ return "[`" + output + "`](#doc_" + BindingInfo::Language() + "_" + ++ ToUnderscores(anchorType) + ")"; ++} ++ ++template ++inline bool IgnoreCheck(const T& t) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::IgnoreCheck(t); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::IgnoreCheck(t); ++ } ++ else ++ { ++ throw std::invalid_argument("IgnoreCheck(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_docs.cpp b/src/mlpack/bindings/markdown/print_docs.cpp +new file mode 100644 +index 000000000..2711a8789 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.cpp +@@ -0,0 +1,218 @@ ++/** ++ * @file print_docs.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of functions to print Markdown from documentation. ++ */ ++#include "print_docs.hpp" ++ ++#include ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++ ++// Make sure that this is defined. ++#ifndef DOXYGEN_PREFIX ++#define DOXYGEN_PREFIX "https://mlpack.org/docs/mlpack-git/doxygen/" ++#endif ++ ++using namespace std; ++using namespace mlpack; ++using namespace mlpack::util; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages) ++{ ++ // We just want to print the name of the function and a link, as Markdown. ++ // We have to mark it as having the right language with a div. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << " - [" << GetBindingName(bindingName) << "](#" << languages[i] ++ << "_" << bindingName << "){: .language-link #" << languages[i] << " }" ++ << endl; ++ } ++} ++ ++void PrintDocs(const std::string& bindingName, ++ const vector& languages) ++{ ++ ProgramDoc& programDoc = BindingInfo::GetProgramDoc(bindingName); ++ ++ CLI::RestoreSettings(bindingName); ++ ++ // First, for this section, print each of the names. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << "## " << GetBindingName(bindingName) << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName << " }" << endl; ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ // Next we want to print the logical name of the binding (that's known by ++ // ProgramInfo). ++ cout << "#### " << programDoc.programName << endl; ++ cout << endl; ++ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << ProgramCall(bindingName); ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ cout << programDoc.shortDocumentation << " "; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "[Detailed documentation](#" << languages[i] << "_" ++ << bindingName << "_detailed-documentation){: .language-detail-link #" ++ << languages[i] << " }"; ++ } ++ cout << "." << endl; ++ ++ // Next, print the PROGRAM_INFO() documentation for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ // This works with the Kramdown processor. ++ cout << "
    " << endl; ++ ++ // We need to print the signature. ++ ++ // Now, iterate through each of the input options. ++ cout << endl; ++ cout << "### Input options" << endl; ++ cout << endl; ++ ++ cout << "| ***name*** | ***type*** | ***description*** | ***default*** |" ++ << endl; ++ cout << "|------------|------------|-------------------|---------------|" ++ << endl; ++ map& parameters = CLI::Parameters(); ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ continue; ++ ++ // There are some special options that don't exist in some languages. ++ if (languages[i] != "python" && it->second.name == "copy_all_inputs") ++ continue; ++ if (languages[i] != "cli" && ++ (it->second.name == "help" || it->second.name == "info" || ++ it->second.name == "version")) ++ continue; ++ ++ // Print name, type, description, default. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; // just a string ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " | "; ++ string def = PrintDefault(it->second.name); ++ if (def.size() > 0) ++ cout << "`" << def << "` |"; ++ else ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ // Next, iterate through the list of output options. ++ cout << "### Output options" << endl; ++ cout << endl; ++ string outputInfo = PrintOutputOptionInfo(); ++ if (outputInfo.size() > 0) ++ cout << outputInfo << endl; ++ cout << endl; ++ cout << "| ***name*** | ***type*** | ***description*** |" << endl; ++ cout << "|------------|------------|-------------------|" << endl; ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print name, type, description. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ cout << "### Detailed documentation" << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName ++ << "_detailed-documentation }" << endl; ++ cout << endl; ++ cout << programDoc.documentation() << endl; ++ cout << endl; ++ ++ cout << "### See also" << endl; ++ cout << endl; ++ for (size_t j = 0; j < programDoc.seeAlso.size(); ++j) ++ { ++ cout << " - " << "["; ++ // We need special processing if the user has specified a binding name ++ // starting with @ (i.e., '@kfn' or similar). ++ if (programDoc.seeAlso[j].first[0] == '@') ++ cout << GetBindingName(programDoc.seeAlso[j].first.substr(1)); ++ else ++ cout << programDoc.seeAlso[j].first; ++ cout << "]("; ++ ++ // We need special handling of Doxygen information. ++ if (programDoc.seeAlso[j].second.substr(0, 8) == "@doxygen") ++ { ++ cout << DOXYGEN_PREFIX << programDoc.seeAlso[j].second.substr(9); ++ } ++ else if (programDoc.seeAlso[j].second[0] == '#') ++ { ++ cout << "#" << languages[i] << "_" ++ << programDoc.seeAlso[j].second.substr(1); ++ } ++ else ++ { ++ cout << programDoc.seeAlso[j].second; ++ } ++ ++ cout << ")" << endl; ++ } ++ cout << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ } ++ ++ CLI::ClearSettings(); ++} +diff --git a/src/mlpack/bindings/markdown/print_docs.hpp b/src/mlpack/bindings/markdown/print_docs.hpp +new file mode 100644 +index 000000000..abbc88398 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.hpp +@@ -0,0 +1,33 @@ ++/** ++ * @file print_docs.hpp ++ * @author Ryan Curtin ++ * ++ * Functions to generate Markdown for documentation of bindings. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++ ++#include ++ ++/** ++ * Given the current settings of CLI, print the header (which will be the ++ * navigation tab) for the binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ */ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages); ++ ++/** ++ * Given the current settings of CLI, print Markdown documentation for the ++ * binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ * ++ * @param bindingName The binding name (${BINDING} from CMake). ++ * @param languages The set of languages to print documentation for. ++ */ ++void PrintDocs(const std::string& bindingName, ++ const std::vector& languages); ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_type_doc.hpp b/src/mlpack/bindings/markdown/print_type_doc.hpp +new file mode 100644 +index 000000000..108461ed5 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_type_doc.hpp +@@ -0,0 +1,46 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, depending on the current language (as ++ * set in BindingInfo). ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++std::string PrintTypeDoc(const util::ParamData& data) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintTypeDoc::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintTypeDoc::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintTypeDoc(): unknown " ++ "BindingInfo::Language()" + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/program_doc_wrapper.hpp b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +new file mode 100644 +index 000000000..21da3ffc0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +@@ -0,0 +1,41 @@ ++/** ++ * @file program_doc_wrapper.hpp ++ * @author Ryan Curtin ++ * ++ * A simple wrapper around ProgramDoc that also calls ++ * BindingInfo::RegisterProgramDoc() upon construction. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++ ++#include "binding_info.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++class ProgramDocWrapper ++{ ++ public: ++ /** ++ * Construct a ProgramDoc object and register it with ++ * BindingInfo::RegisterProgramDoc(). ++ */ ++ ProgramDocWrapper(const std::string& bindingName, ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& ++ seeAlso) ++ { ++ util::ProgramDoc pd(programName, shortDocumentation, documentation, ++ seeAlso); ++ BindingInfo::RegisterProgramDoc(bindingName, pd); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/res/change_language.js b/src/mlpack/bindings/markdown/res/change_language.js +new file mode 100644 +index 000000000..4b520205f +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/change_language.js +@@ -0,0 +1,102 @@ ++/** ++ * A utility function to change the language displayed on the page. This ++ * function should be called whenever the language is changed from the ++ * drop-down. ++ */ ++function changeLanguage() ++{ ++ lang = document.getElementById("language-select").value; ++ var links = document.getElementsByClassName("language-link"); ++ for (i = 0; i < links.length; ++i) ++ { ++ // With each of the links, we get the inner
    , but we need the parent ++ //
  • . ++ if (links[i].id == lang || links[i].id == "always") ++ links[i].parentElement.style.display = "list-item"; ++ else ++ links[i].parentElement.style.display = "none"; ++ } ++ ++ var titles = document.getElementsByClassName("language-title"); ++ for (i = 0; i < titles.length; ++i) ++ { ++ if (titles[i].id == lang) ++ titles[i].style.display = "inline"; ++ else ++ titles[i].style.display = "none"; ++ } ++ ++ var headers = document.getElementsByClassName("language-header"); ++ for (i = 0; i < headers.length; ++i) ++ { ++ if (headers[i].id == lang) ++ headers[i].style.display = "inline"; ++ else ++ headers[i].style.display = "none"; ++ } ++ ++ var decls = document.getElementsByClassName("language-decl"); ++ for (i = 0; i < decls.length; ++i) ++ { ++ if (decls[i].id == lang) ++ decls[i].style.display = "inline"; ++ else ++ decls[i].style.display = "none"; ++ } ++ ++ var types = document.getElementsByClassName("language-types"); ++ for (i = 0; i < types.length; ++i) ++ { ++ if (types[i].id == lang) ++ types[i].style.display = "inline"; ++ else ++ types[i].style.display = "none"; ++ } ++ ++ var details = document.getElementsByClassName("language-detail-link"); ++ for (i = 0; i < details.length; ++i) ++ { ++ if (details[i].id == lang) ++ details[i].style.display = "inline"; ++ else ++ details[i].style.display = "none"; ++ } ++ ++ var sections = document.getElementsByClassName("language-section"); ++ for (i = 0; i < sections.length; ++i) ++ { ++ if (sections[i].id == lang) ++ sections[i].style.display = "inline"; ++ else ++ sections[i].style.display = "none"; ++ } ++} ++ ++document.body.onload = function() ++{ ++ // Do we need to manually set the language because the user came with an ++ // anchor? ++ if (window.location.hash) ++ { ++ // Try to extract the language. ++ firstUnderscore = window.location.hash.indexOf("_"); ++ if (firstUnderscore !== -1) ++ { ++ var lang = window.location.hash.substring(1, firstUnderscore); ++ // Now see if it's in the list of languages. ++ var select = document.getElementById("language-select"); ++ for (i = 0; i < select.length; ++i) ++ { ++ var select_lang = select[i].value; ++ // Is the language a match? ++ if (lang === select_lang) ++ { ++ select.value = select_lang; ++ break; ++ } ++ } ++ } ++ } ++ ++ changeLanguage(); ++} +diff --git a/src/mlpack/bindings/markdown/res/formatting.css b/src/mlpack/bindings/markdown/res/formatting.css +new file mode 100644 +index 000000000..487b026e7 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/formatting.css +@@ -0,0 +1,142 @@ ++body ++{ ++ background: #000000; ++} ++ ++/* Don't display other languages by default. */ ++div.language-title, div.language-header, div.language-decl, ++div.language-detail-link, div.language-types, div.language-section ++{ ++ display: none; ++} ++ ++/* Just display cli documentation. */ ++div.language-title#cli, div.language-header#cli, div.language-decl#cli, ++div.language-detail-link#cli, div.language-types#cli, div.language-section#cli ++{ ++ display: none; ++} ++ ++div#header ++{ ++ padding-top: 10px; ++ width: 200px; ++ background: #000000; ++ border-right: 2px solid #333333; ++ height: 100%; ++ position: fixed; ++ z-index: 1; ++ top: 0; ++ left: 0; ++ overflow-y: scroll; ++} ++ ++div#header .language-select-div select ++{ ++ color: #ffffff; ++ background: transparent; ++ border: none; ++ height: 29px; ++ padding: 5px; ++ width: 190px; ++} ++ ++div#header .language-select-div ++{ ++ color: #ffffff; ++ border: 2px solid #333333; ++ background: url('../res/menu_bg.png') no-repeat 96% 0; ++ height: 34px; ++ width: 180px; ++ overflow: hidden; ++ margin-bottom: 20px; ++ ++ -webkit-border-radius: 5px; ++ -moz-border-radius: 5px; ++ border-radius: 5px; ++} ++ ++div#header ul ++{ ++ padding-top: 5px; ++ margin-bottom: 5px; ++ color: #bb2000; ++ display: table; ++ text-align: left; ++ padding-left: 0px; ++ margin-left: 15px; ++ font-size: 75%; ++} ++ ++div#header ul li ++{ ++ line-height: 1.3em; ++} ++ ++div#header ul li a ++{ ++ color: #eab72c; ++ font-weight: none; ++} ++ ++div#header ul li a:hover ++{ ++ color: #ffffff; ++ font-weight: none; ++} ++ ++div#docs ++{ ++ margin-left: 200px; ++} ++ ++span.special ++{ ++ font-size: 85%; ++ color: #eab72c; ++} ++ ++div.category ++{ ++ text-align: left; ++} ++ ++div.category h5 ++{ ++ text-align: left; ++ margin-top: 0; ++ margin-bottom: 0; ++ color: #ffffff; ++ font-size: 100%; ++ font-weight: normal; ++ padding: 0; ++} ++ ++h2#mlpack-overview, h2.language-types-h2 ++{ ++ padding-bottom: 0.75em; ++} ++ ++div.language-header h1 ++{ ++ color: #ffffff; ++ text-align: center; ++ border-top: none; ++ border-bottom: 1px solid #eab72c; ++} ++ ++div.language-types li code ++{ ++ font-weight: bold; ++ color: #eab72c; ++} ++ ++div.language-types li ++{ ++ padding-bottom: 1em; ++} ++ ++div.language-section table td a code:hover ++{ ++ color: #bb2000; ++} +diff --git a/src/mlpack/bindings/markdown/res/menu_bg.png b/src/mlpack/bindings/markdown/res/menu_bg.png +new file mode 100644 +index 0000000000000000000000000000000000000000..c29580a400267755c0f2a7d1b8f154d329797553 +GIT binary patch +literal 395 +zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I +zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@$z$e7@|Ns9$rm%z} +zkO5&YUc4A6vSP&wAh~+=Y9P6B<3=C}Q4qtiwFt=JD+%%oW?-oBD6-sRv)}%J<6##S +z{uAOr8O9`UcNcz%T?{vY9QG1VUsv|Wj9gsIB1StofUaP$^K@|xk+_^3kmk^E@KBlv +z3nQb?wiDJ01q~ZqKi}J1EyfTU{PA6^wa?xsyTXMZR(+r2Hbc2uaP~}(9I0R241Yd| +zZn0k0^$2K^YKdz^NlIc#s#S7PDv)9@GBC8%H89jQGzc*?wK6caGBVIL05S}G9 ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. This is for regular types. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a matrix option, a tuple option, a ++ * serializable option, or a string option (this returns the default filename, ++ * or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */ = 0); ++ ++/** ++ * Return the default value of a model option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of an option. This is the function that will be ++ * placed into the CLI functionMap. ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ std::string* outstr = (std::string*) output; ++ *outstr = DefaultParamImpl::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "default_param_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/default_param_impl.hpp b/src/mlpack/bindings/python/default_param_impl.hpp +new file mode 100644 +index 000000000..bae57cce2 +--- /dev/null ++++ b/src/mlpack/bindings/python/default_param_impl.hpp +@@ -0,0 +1,147 @@ ++/** ++ * @file default_param_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the default value of a parameter, depending on its type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++ ++#include "default_param.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type* /* junk */) ++{ ++ std::ostringstream oss; ++ if (std::is_same::value) ++ oss << "False"; ++ else ++ oss << boost::any_cast(data.value); ++ ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ // Print each element in an array delimited by square brackets. ++ std::ostringstream oss; ++ const T& vector = boost::any_cast(data.value); ++ oss << "["; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ const std::string& s = *boost::any_cast(&data.value); ++ return "'" + s + "'"; ++} ++ ++/** ++ * Return the default value of a matrix option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */) ++{ ++ // Get the filename and return it, or return an empty string. ++ if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "np.empty([0])"; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "np.empty([0], dtype=np.uint64)"; ++ } ++ else if (std::is_same>::value) ++ { ++ return "np.empty([0, 0], dtype=np.uint64)"; ++ } ++ else ++ { ++ return "np.empty([0, 0])"; ++ } ++} ++ ++/** ++ * Return the default value of a model option (always "None"). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ return "None"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_cython_type.hpp b/src/mlpack/bindings/python/get_cython_type.hpp +index e44f4f70b..e44b91eb0 100644 +--- a/src/mlpack/bindings/python/get_cython_type.hpp ++++ b/src/mlpack/bindings/python/get_cython_type.hpp +@@ -40,16 +40,6 @@ inline std::string GetCythonType( + return "int"; + } + +-template<> +-inline std::string GetCythonType( +- const util::ParamData& /* d */, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*) +-{ +- return "float"; +-} +- + template<> + inline std::string GetCythonType( + const util::ParamData& /* d */, +diff --git a/src/mlpack/bindings/python/get_printable_type.hpp b/src/mlpack/bindings/python/get_printable_type.hpp +new file mode 100644 +index 000000000..51a528b4d +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type.hpp +@@ -0,0 +1,120 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++void GetPrintableType(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(d); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_printable_type_impl.hpp b/src/mlpack/bindings/python/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..2031b3435 +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type_impl.hpp +@@ -0,0 +1,151 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "unknown"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "int"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "double"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "string"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "size_t"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "bool"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "list of " + GetPrintableType(d) + "s"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ std::string type = "matrix"; ++ if (std::is_same::value) ++ { ++ if (T::is_row || T::is_col) ++ type = "vector"; ++ } ++ else if (std::is_same::value) ++ { ++ type = "int matrix"; ++ if (T::is_row || T::is_col) ++ type = "int vector"; ++ } ++ ++ return type; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type*) ++{ ++ return "categorical matrix"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return d.cppType + "Type"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_doc.hpp b/src/mlpack/bindings/python/print_doc.hpp +index 7a5a27781..b8962ccaf 100644 +--- a/src/mlpack/bindings/python/print_doc.hpp ++++ b/src/mlpack/bindings/python/print_doc.hpp +@@ -14,7 +14,7 @@ + + #include + #include +-#include "get_python_type.hpp" ++#include "get_printable_type.hpp" + + namespace mlpack { + namespace bindings { +@@ -44,24 +44,20 @@ void PrintDoc(const util::ParamData& d, + oss << d.name << "_ ("; + else + oss << d.name << " ("; +- oss << GetPythonType::type>(d) << "): " ++ oss << GetPrintableType::type>(d) << "): " + << d.desc; + + // Print a default, if possible. + if (!d.required) + { +- if (d.cppType == "std::string") ++ // Call the correct overload to get the default value directly. ++ if (d.cppType == "std::string" || d.cppType == "double" || ++ d.cppType == "int" || d.cppType == "std::vector" || ++ d.cppType == "std::vector" || ++ d.cppType == "std::vector") + { +- oss << " Default value '" << boost::any_cast(d.value) +- << "'."; +- } +- else if (d.cppType == "double") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; +- } +- else if (d.cppType == "int") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; ++ std::string defaultValue = DefaultParamImpl(d); ++ oss << " Default value " << defaultValue << "."; + } + } + +diff --git a/src/mlpack/bindings/python/print_doc_functions.hpp b/src/mlpack/bindings/python/print_doc_functions.hpp +index a9207c570..51e85b78d 100644 +--- a/src/mlpack/bindings/python/print_doc_functions.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions.hpp +@@ -19,6 +19,21 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -29,6 +44,11 @@ inline std::string PrintValue(const T& value, bool quotes); + template<> + inline std::string PrintValue(const bool& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + // Recursion base case. + inline std::string PrintInputOptions(); + +@@ -57,6 +77,12 @@ std::string PrintOutputOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +diff --git a/src/mlpack/bindings/python/print_doc_functions_impl.hpp b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +index c169f72c8..3ff526011 100644 +--- a/src/mlpack/bindings/python/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +@@ -19,6 +19,32 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ // No modification is needed to the name---we just use it as-is. ++ return bindingName + "()"; ++} ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ return "from mlpack import " + bindingName; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return "Results are returned in a Python dictionary. The keys of the " ++ "dictionary are the names of the output parameters."; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -48,6 +74,23 @@ inline std::string PrintValue(const bool& value, bool quotes) + return "False"; + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + // Recursion base case. + std::string PrintInputOptions() { return ""; } + +@@ -136,7 +179,8 @@ std::string PrintOutputOptions(const std::string& paramName, + + /** + * Given a name of a binding and a variable number of arguments (and their +- * contents), print the corresponding function call. ++ * contents), print the corresponding function call. The given programName ++ * should not be the output of GetBindingName(). + */ + template + std::string ProgramCall(const std::string& programName, Args... args) +@@ -159,6 +203,76 @@ std::string ProgramCall(const std::string& programName, Args... args) + return util::HyphenateString(call, 2) + "\n" + oss.str(); + } + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. The programName should not be the output of GetBindingName(). ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << ">>> "; ++ ++ // Determine if we have any output options. ++ const std::map& parameters = CLI::Parameters(); ++ bool hasOutput = false; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ { ++ hasOutput = true; ++ break; ++ } ++ } ++ ++ if (hasOutput) ++ oss << "d = "; ++ ++ oss << programName << "("; ++ ++ // Now iterate over every input option. ++ bool first = true; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ if (!first) ++ oss << ", "; ++ else ++ first = false; ++ ++ // Print the input option. ++ if (it->second.name != "lambda") // Don't print Python keywords. ++ oss << it->second.name << "="; ++ else ++ oss << it->second.name << "_="; ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ oss << value; ++ } ++ oss << ")"; ++ ++ std::string result = util::HyphenateString(oss.str(), 8); ++ ++ oss.str(""); ++ oss << result; ++ ++ // Now print output lines. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print a new line for the output option. ++ oss << std::endl << ">>> " << it->second.name << " = d['" ++ << it->second.name << "']"; ++ } ++ ++ return oss.str(); ++} ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +@@ -176,14 +290,6 @@ inline std::string PrintDataset(const std::string& datasetName) + return "'" + datasetName + "'"; + } + +-/** +- * Given the name of a binding, print its invocation. +- */ +-inline std::string ProgramCall(const std::string& programName) +-{ +- return ">>> " + programName + "("; +-} +- + /** + * Print any closing call to a program. For a Python binding this is a closing + * brace. +diff --git a/src/mlpack/bindings/python/print_input_processing.hpp b/src/mlpack/bindings/python/print_input_processing.hpp +index e271906cb..04f318a7c 100644 +--- a/src/mlpack/bindings/python/print_input_processing.hpp ++++ b/src/mlpack/bindings/python/print_input_processing.hpp +@@ -17,7 +17,6 @@ + #include "get_numpy_type.hpp" + #include "get_numpy_type_char.hpp" + #include "get_cython_type.hpp" +-#include "get_python_type.hpp" + #include "strip_type.hpp" + + namespace mlpack { +diff --git a/src/mlpack/bindings/python/print_type_doc.hpp b/src/mlpack/bindings/python/print_type_doc.hpp +new file mode 100644 +index 000000000..9a5d6dc71 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_type_doc_impl.hpp b/src/mlpack/bindings/python/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..8d6ae3971 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc_impl.hpp +@@ -0,0 +1,162 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option (True or False)."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A list of integers; i.e., [0, 1, 2]."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A list of strings; i.e., [\"hello\", \"goodbye\"]."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data. This can be a 2-d matrix where " ++ "one dimension has size 1, or it can also be a list, a numpy 1-d " ++ "ndarray, or a 1-d pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data. This can be a list of lists, a " ++ "numpy ndarray, or a pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ } ++ else if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data with a uint64 dtype. This can be" ++ " a 2-d matrix where one dimension has size 1, or it can also be a " ++ "list, a numpy 1-d ndarray, or a 1-d pandas DataFrame. If the dtype " ++ "is not already uint64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data with a uint64 dtype. This can " ++ "be a list of lists, a numpy ndarray, or a pandas DataFrame. If the " ++ "dtype is not already uint64, it will be converted."; ++ } ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A 2-d arraylike containing data. Like the regular 2-d matrices, this" ++ " can be a list of lists, a numpy ndarray, or a pandas DataFrame. " ++ "However, this type can also accept a pandas DataFrame that has columns " ++ "of type 'CategoricalDtype'. These categorical values will be converted " ++ "to numeric indices before being passed to mlpack, and then inside mlpack" ++ " they will be properly treated as categorical variables, so there is no " ++ "need to do one-hot encoding for this matrix type. If the dtype of the " ++ "given matrix is not already float64, it will be converted."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "An mlpack model pointer. This type can be pickled to or from disk, " ++ "and internally holds a pointer to C++ memory containing the mlpack " ++ "model. Note that this means that the mlpack model itself cannot be " ++ "easily inspected in Python; however, the pickled model can be loaded " ++ "in C++ and inspected there."; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/py_option.hpp b/src/mlpack/bindings/python/py_option.hpp +index 98bba2dd5..e175d282b 100644 +--- a/src/mlpack/bindings/python/py_option.hpp ++++ b/src/mlpack/bindings/python/py_option.hpp +@@ -13,6 +13,7 @@ + #define MLPACK_BINDINGS_PYTHON_PY_OPTION_HPP + + #include ++#include "default_param.hpp" + #include "get_param.hpp" + #include "get_printable_param.hpp" + #include "print_class_defn.hpp" +@@ -85,6 +86,9 @@ class PyOption + CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = + &GetPrintableParam; + ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ + // These are used by the pyx generator. + CLI::GetSingleton().functionMap[data.tname]["PrintClassDefn"] = + &PrintClassDefn; +diff --git a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +index cf3c1055d..93daaddc4 100644 +--- a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp ++++ b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +@@ -19,6 +19,7 @@ using namespace mlpack; + using namespace mlpack::kernel; + + PROGRAM_INFO("Python binding test", ++ "A simple program to test Python binding functionality.", + "A simple program to test Python binding functionality. You can build " + "mlpack with the BUILD_TESTS option set to off, and this binding will " + "no longer be built."); +diff --git a/src/mlpack/core/util/cli.cpp b/src/mlpack/core/util/cli.cpp +index f2bf6cec2..1a83080bd 100644 +--- a/src/mlpack/core/util/cli.cpp ++++ b/src/mlpack/core/util/cli.cpp +@@ -27,7 +27,8 @@ using namespace mlpack; + using namespace mlpack::util; + + // Fake ProgramDoc in case none is supplied. +-static ProgramDoc emptyProgramDoc = ProgramDoc("", []() { return ""; }); ++static ProgramDoc emptyProgramDoc = ProgramDoc("", "", []() { return ""; }, ++ {}); + + /* Constructors, Destructors, Copy */ + /* Make the constructor private, to preclude unauthorized instances */ +diff --git a/src/mlpack/core/util/mlpack_main.hpp b/src/mlpack/core/util/mlpack_main.hpp +index 4c915c0c0..8003f38ac 100644 +--- a/src/mlpack/core/util/mlpack_main.hpp ++++ b/src/mlpack/core/util/mlpack_main.hpp +@@ -21,6 +21,7 @@ + #define BINDING_TYPE_CLI 0 + #define BINDING_TYPE_TEST 1 + #define BINDING_TYPE_PYX 2 ++#define BINDING_TYPE_MARKDOWN 128 + #define BINDING_TYPE_UNKNOWN -1 + + #ifndef BINDING_TYPE +@@ -99,9 +100,10 @@ using Option = mlpack::bindings::tests::TestOption; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }) + + #elif(BINDING_TYPE == BINDING_TYPE_PYX) // This is a Python binding. + +@@ -128,9 +130,10 @@ static const std::string testName = ""; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); \ ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }); \ + namespace mlpack { \ + namespace bindings { \ + namespace python { \ +@@ -148,6 +151,59 @@ PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" + + // Nothing else needs to be defined---the binding will use mlpackMain() as-is. + ++#elif BINDING_TYPE == BINDING_TYPE_MARKDOWN ++ ++// It doesn't really matter whether this is true or false... ++#define BINDING_MATRIX_TRANSPOSED true ++ ++// We use BINDING_NAME in PROGRAM_INFO() so it needs to be defined. ++#ifndef BINDING_NAME ++ #error "BINDING_NAME must be defined when BINDING_TYPE is Markdown!" ++#endif ++ ++#include ++#include ++ ++#define PRINT_PARAM_STRING mlpack::bindings::markdown::ParamString ++#define PRINT_PARAM_VALUE mlpack::bindings::markdown::PrintValue ++#define PRINT_DATASET mlpack::bindings::markdown::PrintDataset ++#define PRINT_MODEL mlpack::bindings::markdown::PrintModel ++#define PRINT_CALL mlpack::bindings::markdown::ProgramCall ++#define BINDING_IGNORE_CHECK mlpack::bindings::markdown::IgnoreCheck ++ ++namespace mlpack { ++namespace util { ++ ++template ++using Option = mlpack::bindings::markdown::MDOption; ++ ++} ++} ++ ++#include ++#include ++ ++#undef PROGRAM_INFO ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) static \ ++ mlpack::bindings::markdown::ProgramDocWrapper \ ++ cli_programdoc_dummy_object = \ ++ mlpack::bindings::markdown::ProgramDocWrapper(BINDING_NAME, NAME, \ ++ SHORT_DESC, []() { return DESC; }, { __VA_ARGS__ }); \ ++ ++PARAM_FLAG("verbose", "Display informational messages and the full list of " ++ "parameters and timers at the end of execution.", "v"); ++ ++// CLI-specific parameters. ++PARAM_FLAG("help", "Default help info.", "h"); ++PARAM_STRING_IN("info", "Print help on a specific option.", "", ""); ++PARAM_FLAG("version", "Display the version of mlpack.", "V"); ++ ++// Python-specific parameters. ++PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" ++ " copied before the method is run. This is useful for debugging problems " ++ "where the input parameters are being modified by the algorithm, but can " ++ "slow down the code.", ""); ++ + #else + + #error "Unknown binding type! Be sure BINDING_TYPE is defined if you are " \ +diff --git a/src/mlpack/core/util/param.hpp b/src/mlpack/core/util/param.hpp +index b812887fb..de50f5b00 100644 +--- a/src/mlpack/core/util/param.hpp ++++ b/src/mlpack/core/util/param.hpp +@@ -29,6 +29,21 @@ using DatasetInfo = DatasetMapper; + } // namespace data + } // namespace mlpack + ++/** ++ * Provide a link for a binding's "see also" documentation section, which is ++ * primarily (but not necessarily exclusively) used by the Markdown bindings ++ * This link can be specified by calling SEE_ALSO("description", "link"), where ++ * "description" is the description of the link and "link" may be one of the ++ * following: ++ * ++ * - A direct URL, starting with http:// or https://. ++ * - A page anchor for documentation, referencing another binding by its CMake ++ * binding name, i.e. "#knn". ++ * - A link to a Doxygen page, using the mangled Doxygen name after a ++ * '@doxygen/', i.e., "@doxygen/mlpack1_1_adaboost1_1_AdaBoost". ++ */ ++#define SEE_ALSO(DESCRIPTION, LINK) {DESCRIPTION, LINK} ++ + /** + * Document an executable. Only one instance of this macro should be + * present in your program! Therefore, use it in the main.cpp +@@ -41,14 +56,22 @@ using DatasetInfo = DatasetMapper; + * PARAM_DOUBLE_OUT_REQ(), PARAM_VECTOR_OUT_REQ(), PARAM_STRING_OUT_REQ(). + * + * @param NAME Short string representing the name of the program. ++ * @param SHORT_DESC Short two-sentence description of the program; it should ++ * describe what the program implements and does, and a quick overview of ++ * how it can be used and what it should be used for. + * @param DESC Long string describing what the program does and possibly a + * simple usage example. Newlines should not be used here; this is taken + * care of by CLI (however, you can explicitly specify newlines to denote +- * new paragraphs). ++ * new paragraphs). You can also use printing macros like ++ * PRINT_PARAM_STRING(), PRINT_DATASET(), and others. ++ * @param SEE_ALSOS A set of SEE_ALSO() macros that are used for generating ++ * documentation. See the SEE_ALSO() macro. This is a varargs argument, so ++ * you can add as many SEE_ALSO()s as you like. + */ +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }) ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ } ) + + /** + * Define a flag parameter. +diff --git a/src/mlpack/core/util/program_doc.cpp b/src/mlpack/core/util/program_doc.cpp +index 814573b1c..28d7ee8b8 100644 +--- a/src/mlpack/core/util/program_doc.cpp ++++ b/src/mlpack/core/util/program_doc.cpp +@@ -23,17 +23,33 @@ using namespace std; + * Construct a ProgramDoc object. When constructed, it will register itself + * with CLI. A fatal error will be thrown if more than one is constructed. + * +- * @param programName Short string representing the name of the program. +- * @param documentation Long string containing documentation on how to use the +- * program and what it is. No newline characters are necessary; this is +- * taken care of by CLI later. + * @param defaultModule Name of the default module. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. ++ * @param documentation Long string containing documentation on how to use the ++ * program and what it is. No newline characters are necessary; this is ++ * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ +-ProgramDoc::ProgramDoc(const std::string& programName, +- const std::function& documentation) : ++ProgramDoc::ProgramDoc( ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso) : + programName(programName), +- documentation(documentation) ++ shortDocumentation(shortDocumentation), ++ documentation(documentation), ++ seeAlso(seeAlso) + { + // Register this with CLI. + CLI::RegisterProgramDoc(this); + } ++ ++/** ++ * Construct an empty ProgramDoc object. ++ */ ++ProgramDoc::ProgramDoc() ++{ ++ CLI::RegisterProgramDoc(this); ++} +diff --git a/src/mlpack/core/util/program_doc.hpp b/src/mlpack/core/util/program_doc.hpp +index f5de89fe5..296b15586 100644 +--- a/src/mlpack/core/util/program_doc.hpp ++++ b/src/mlpack/core/util/program_doc.hpp +@@ -33,17 +33,32 @@ class ProgramDoc + * will be returned. + * + * @param programName Short string representing the name of the program. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. + * @param documentation Long string containing documentation on how to use the + * program and what it is. No newline characters are necessary; this is + * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ + ProgramDoc(const std::string& programName, +- const std::function& documentation); ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso); ++ ++ /** ++ * Construct an empty ProgramDoc object. (This is not meant to be used!) ++ */ ++ ProgramDoc(); + + //! The name of the program. + std::string programName; ++ //! The short documentation for the program. ++ std::string shortDocumentation; + //! Documentation for what the program does. + std::function documentation; ++ //! Set of see also information. ++ std::vector> seeAlso; + }; + + } // namespace util +diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt +index 4e6fc3df9..360e79f93 100644 +--- a/src/mlpack/methods/CMakeLists.txt ++++ b/src/mlpack/methods/CMakeLists.txt +@@ -74,3 +74,7 @@ endforeach() + + set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) ++set(MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++set(MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++set(MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) +diff --git a/src/mlpack/methods/adaboost/CMakeLists.txt b/src/mlpack/methods/adaboost/CMakeLists.txt +index 5505f057f..3e85237e0 100644 +--- a/src/mlpack/methods/adaboost/CMakeLists.txt ++++ b/src/mlpack/methods/adaboost/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(adaboost) + add_python_binding(adaboost) ++add_markdown_docs(adaboost "cli;python" "classification") +diff --git a/src/mlpack/methods/adaboost/adaboost_main.cpp b/src/mlpack/methods/adaboost/adaboost_main.cpp +index 2634ff08a..db2656353 100644 +--- a/src/mlpack/methods/adaboost/adaboost_main.cpp ++++ b/src/mlpack/methods/adaboost/adaboost_main.cpp +@@ -46,7 +46,14 @@ using namespace mlpack::decision_stump; + using namespace mlpack::perceptron; + using namespace mlpack::util; + +-PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " ++PROGRAM_INFO("AdaBoost", ++ // Short description. ++ "An implementation of the AdaBoost.MH (Adaptive Boosting) algorithm for " ++ "classification. This can be used to train an AdaBoost model on labeled " ++ "data or use an existing AdaBoost model to predict the classes of new " ++ "points.", ++ // Long description. ++ "This program implements the AdaBoost (or Adaptive " + "Boosting) algorithm. The variant of AdaBoost implemented here is " + "AdaBoost.MH. It uses a weak learner, either decision stumps or " + "perceptrons, and over many iterations, creates a strong learner that is a " +@@ -72,7 +79,7 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + "classes for each point in the test dataset are output to the " + + PRINT_PARAM_STRING("output") + " output parameter. The AdaBoost model " + "itself is output to the " + PRINT_PARAM_STRING("output_model") + +- "output parameter." ++ " output parameter." + "\n\n" + "For example, to run AdaBoost on an input dataset " + + PRINT_DATASET("data") + " with perceptrons as the weak learner type, " +@@ -88,7 +95,15 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + PRINT_DATASET("predictions") + " with the following command: " + "\n\n" + + PRINT_CALL("adaboost", "input_model", "model", "test", "test_data", +- "output", "predictions")); ++ "output", "predictions"), ++ // See also... ++ SEE_ALSO("AdaBoost on Wikipedia", "https://en.wikipedia.org/wiki/AdaBoost"), ++ SEE_ALSO("Improved boosting algorithms using confidence-rated predictions " ++ "(pdf)", "http://rob.schapire.net/papers/SchapireSi98.pdf"), ++ SEE_ALSO("Perceptron", "#perceptron"), ++ SEE_ALSO("Decision Stump", "#decision_stump"), ++ SEE_ALSO("mlpack::adaboost::AdaBoost C++ class documentation", ++ "@doxygen/classmlpack_1_1adaboost_1_1AdaBoost.html")); + + // Input for training. + PARAM_MATRIX_IN("training", "Dataset for training AdaBoost.", "t"); +diff --git a/src/mlpack/methods/approx_kfn/CMakeLists.txt b/src/mlpack/methods/approx_kfn/CMakeLists.txt +index da20af02b..be1a5c4f0 100644 +--- a/src/mlpack/methods/approx_kfn/CMakeLists.txt ++++ b/src/mlpack/methods/approx_kfn/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # This program computes approximate furthest neighbors. + add_cli_executable(approx_kfn) + add_python_binding(approx_kfn) ++add_markdown_docs(approx_kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +index 9adce050e..7091536fd 100644 +--- a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp ++++ b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +@@ -22,6 +22,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Approximate furthest neighbor search", ++ // Short description. ++ "An implementation of two strategies for furthest neighbor search. This " ++ "can be used to compute the furthest neighbor of query point(s) from a set " ++ "of points; furthest neighbor models can be saved and reused with future " ++ "query point(s).", ++ // Long description. + "This program implements two strategies for furthest neighbor search. " + "These strategies are:" + "\n\n" +@@ -86,7 +92,18 @@ PROGRAM_INFO("Approximate furthest neighbor search", + PRINT_DATASET("neighbors") + " by calling" + "\n\n" + + PRINT_CALL("approx_kfn", "input_model", "model", "query", "new_query_set", +- "k", 3, "neighbors", "neighbors")); ++ "k", 3, "neighbors", "neighbors"), ++ SEE_ALSO("k-furthest-neighbor search", "#kfn"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Fast approximate furthest neighbors with data-dependent candidate" ++ " selection (pdf)", "http://ratml.org/pub/pdf/2016fast.pdf"), ++ SEE_ALSO("Approximate furthest neighbor in high dimensions (pdf)", ++ "https://pdfs.semanticscholar.org/a4b5/7b9cbf37201fb1d9a56c0f4eefad0466" ++ "9c20.pdf"), ++ SEE_ALSO("mlpack::neighbor::QDAFN class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1QDAFN.html"), ++ SEE_ALSO("mlpack::neighbor::DrusillaSelect class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1DrusillaSelect.html")); + + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); + PARAM_MATRIX_IN("query", "Matrix containing query points.", "q"); +diff --git a/src/mlpack/methods/cf/CMakeLists.txt b/src/mlpack/methods/cf/CMakeLists.txt +index 01796f29d..976304458 100644 +--- a/src/mlpack/methods/cf/CMakeLists.txt ++++ b/src/mlpack/methods/cf/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(cf) + add_python_binding(cf) ++add_markdown_docs(cf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp +index 2676975d1..b47f444df 100644 +--- a/src/mlpack/methods/cf/cf_main.cpp ++++ b/src/mlpack/methods/cf/cf_main.cpp +@@ -26,7 +26,13 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " ++PROGRAM_INFO("Collaborative Filtering", ++ // Short description. ++ "An implementation of several collaborative filtering (CF) techniques for " ++ "recommender systems. This can be used to train a new CF model, or use an" ++ " existing CF model to compute recommendations.", ++ // Long description. ++ "This program performs collaborative " + "filtering (CF) on the given dataset. Given a list of user, item and " + "preferences (the " + PRINT_PARAM_STRING("training") + " parameter), " + "the program will perform a matrix decomposition and then can perform a " +@@ -49,7 +55,7 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "addition, the number of recommendations per user to generate can be " + "specified with the " + PRINT_PARAM_STRING("recommendations") + " " + "parameter, and the number of similar users (the size of the neighborhood) " +- " to be considered when generating recommendations can be specified with " ++ "to be considered when generating recommendations can be specified with " + "the " + PRINT_PARAM_STRING("neighborhood") + " parameter." + "\n\n" + "For performing the matrix decomposition, the following optimization " +@@ -79,7 +85,20 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "call " + "\n\n" + + PRINT_CALL("cf", "input_model", "model", "query", "users", +- "recommendations", 5, "output", "recommendations")); ++ "recommendations", 5, "output", "recommendations"), ++ SEE_ALSO("Collaborative filtering tutorial", "@doxygen/cftutorial.html"), ++ SEE_ALSO("Alternating Matrix Factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Collaborative Filtering on Wikipedia", ++ "https://en.wikipedia.org/wiki/Collaborative_filtering"), ++ SEE_ALSO("Matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Matrix_factorization_" ++ "(recommender_systems)"), ++ SEE_ALSO("Matrix factorization techniques for recommender systems (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.441.3234" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::cf::CFType class documentation", ++ "@doxygen/classmlpack_1_1cf_1_1CFType.html")); + + // Parameters for training a model. + PARAM_MATRIX_IN("training", "Input dataset to perform CF on.", "t"); +diff --git a/src/mlpack/methods/dbscan/CMakeLists.txt b/src/mlpack/methods/dbscan/CMakeLists.txt +index 70939c9d0..47b79d782 100644 +--- a/src/mlpack/methods/dbscan/CMakeLists.txt ++++ b/src/mlpack/methods/dbscan/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(dbscan) + add_python_binding(dbscan) ++add_markdown_docs(dbscan "cli;python" "clustering") +diff --git a/src/mlpack/methods/dbscan/dbscan_main.cpp b/src/mlpack/methods/dbscan/dbscan_main.cpp +index c4afc3c85..04bcfc85f 100644 +--- a/src/mlpack/methods/dbscan/dbscan_main.cpp ++++ b/src/mlpack/methods/dbscan/dbscan_main.cpp +@@ -27,6 +27,10 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("DBSCAN clustering", ++ // Short description. ++ "An implementation of DBSCAN clustering. Given a dataset, this can " ++ "compute and return a clustering of that dataset.", ++ // Long description. + "This program implements the DBSCAN algorithm for clustering using " + "accelerated tree-based range search. The type of tree that is used " + "may be parameterized, or brute-force range search may also be used." +@@ -59,7 +63,13 @@ PROGRAM_INFO("DBSCAN clustering", + PRINT_DATASET("input") + " with a radius of 0.5 and a minimum cluster size" + " of 5 is given below:" + "\n\n" + +- PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5)); ++ PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5), ++ SEE_ALSO("DBSCAN on Wikipedia", "https://en.wikipedia.org/wiki/DBSCAN"), ++ SEE_ALSO("A density-based algorithm for discovering clusters in large " ++ "spatial databases with noise (pdf)", ++ "http://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf"), ++ SEE_ALSO("mlpack::dbscan::DBSCAN class documentation", ++ "@doxygen/classmlpack_1_1dbscan_1_1DBSCAN.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to cluster.", "i"); + PARAM_UROW_OUT("assignments", "Output matrix for assignments of each " +diff --git a/src/mlpack/methods/decision_stump/CMakeLists.txt b/src/mlpack/methods/decision_stump/CMakeLists.txt +index e9d9c04c1..a11b0048e 100644 +--- a/src/mlpack/methods/decision_stump/CMakeLists.txt ++++ b/src/mlpack/methods/decision_stump/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_stump) + add_python_binding(decision_stump) ++add_markdown_docs(decision_stump "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_stump/decision_stump_main.cpp b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +index aa4d9a67d..6f8f308af 100644 +--- a/src/mlpack/methods/decision_stump/decision_stump_main.cpp ++++ b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +@@ -22,6 +22,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Decision Stump", ++ // Short description. ++ "An implementation of a decision stump, which is a single-level decision " ++ "tree. Given labeled data, a new decision stump can be trained; or, an " ++ "existing decision stump can be used to classify points.", ++ // Long description. + "This program implements a decision stump, which is a single-level decision" + " tree. The decision stump will split on one dimension of the input data, " + "and will split into multiple buckets. The dimension and bins are selected" +@@ -61,7 +66,12 @@ PROGRAM_INFO("Decision Stump", + "\n\n" + "After training, a decision stump can be saved with the " + + PRINT_PARAM_STRING("output_model") + " output parameter. That stump may " +- "later be re-used in subsequent calls to this program (or others)."); ++ "later be re-used in subsequent calls to this program (or others).", ++ SEE_ALSO("Decision tree", "#decision_tree"), ++ SEE_ALSO("Decision stumps on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_stump"), ++ SEE_ALSO("mlpack::decision_stump::DecisionStump class documentation", ++ "@doxygen/classmlpack_1_1decision__stump_1_1DecisionStump.html")); + + // Datasets we might load. + PARAM_MATRIX_IN("training", "The dataset to train on.", "t"); +diff --git a/src/mlpack/methods/decision_tree/CMakeLists.txt b/src/mlpack/methods/decision_tree/CMakeLists.txt +index 448e4d62e..3f46f0d95 100644 +--- a/src/mlpack/methods/decision_tree/CMakeLists.txt ++++ b/src/mlpack/methods/decision_tree/CMakeLists.txt +@@ -27,3 +27,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_tree) + add_python_binding(decision_tree) ++add_markdown_docs(decision_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_tree/decision_tree_main.cpp b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +index 258689fb6..99a98df4e 100644 +--- a/src/mlpack/methods/decision_tree/decision_tree_main.cpp ++++ b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +@@ -21,6 +21,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Decision tree", ++ // Short description. ++ "An implementation of an ID3-style decision tree for classification, which" ++ " supports categorical data. Given labeled data with numeric or " ++ "categorical features, a decision tree can be trained and saved; or, an " ++ "existing decision tree can be used for classification on new points.", ++ // Long description. + "Train and evaluate using a decision tree. Given a dataset containing " + "numeric or categorical features, and associated labels for each point in " + "the dataset, this program can train a decision tree on that data." +@@ -70,7 +76,15 @@ PROGRAM_INFO("Decision tree", + PRINT_DATASET("predictions") + ", one could call " + "\n\n" + + PRINT_CALL("decision_tree", "input_model", "tree", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("Decision stump", "#decision_stump"), ++ SEE_ALSO("Random forest", "#random_forest"), ++ SEE_ALSO("Decision trees on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_tree_learning"), ++ SEE_ALSO("Induction of Decision Trees (pdf)", ++ "https://link.springer.com/content/pdf/10.1007/BF00116251.pdf"), ++ SEE_ALSO("mlpack::tree::DecisionTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1DecisionTree.html")); + + // Datasets. + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", +diff --git a/src/mlpack/methods/det/CMakeLists.txt b/src/mlpack/methods/det/CMakeLists.txt +index 762c7b11c..58b402c7c 100644 +--- a/src/mlpack/methods/det/CMakeLists.txt ++++ b/src/mlpack/methods/det/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(det) + add_python_binding(det) ++add_markdown_docs(det "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp +index a9cb4ec47..07901d1bd 100644 +--- a/src/mlpack/methods/det/det_main.cpp ++++ b/src/mlpack/methods/det/det_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Density Estimation With Density Estimation Trees", ++ // Short description. ++ "An implementation of density estimation trees for the density estimation " ++ "task. Density estimation trees can be trained or used to predict the " ++ "density at locations given by query points.", ++ // Long description. + "This program performs a number of functions related to Density Estimation " + "Trees. The optimal Density Estimation Tree (DET) can be trained on a set " + "of data (specified by " + PRINT_PARAM_STRING("training") + ") using " +@@ -49,7 +54,15 @@ PROGRAM_INFO("Density Estimation With Density Estimation Trees", + "trained on the given training points, or a tree given as the parameter " + + PRINT_PARAM_STRING("input_model") + ". The density estimates for the test" + " points may be saved using the " + +- PRINT_PARAM_STRING("test_set_estimates") + " output parameter."); ++ PRINT_PARAM_STRING("test_set_estimates") + " output parameter.", ++ SEE_ALSO("Density estimation tree (DET) tutorial", ++ "@doxygen/dettutorial.html"), ++ SEE_ALSO("Density estimation on Wikipedia", ++ "https://en.wikipedia.org/wiki/Density_estimation"), ++ SEE_ALSO("Density estimation trees (pdf)", ++ "http://www.mlpack.org/papers/det.pdf"), ++ SEE_ALSO("mlpack::tree::DTree class documentation", ++ "@doxygen/classmlpack_1_1det_1_1DTree.html")); + + // Input data files. + PARAM_MATRIX_IN("training", "The data set on which to build a density " +diff --git a/src/mlpack/methods/emst/CMakeLists.txt b/src/mlpack/methods/emst/CMakeLists.txt +index 73cd431cb..7a073a8f9 100644 +--- a/src/mlpack/methods/emst/CMakeLists.txt ++++ b/src/mlpack/methods/emst/CMakeLists.txt +@@ -23,3 +23,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(emst) + add_python_binding(emst) ++add_markdown_docs(emst "cli;python" "geometry") +diff --git a/src/mlpack/methods/emst/emst_main.cpp b/src/mlpack/methods/emst/emst_main.cpp +index 3b0f9de98..e60ba7b64 100644 +--- a/src/mlpack/methods/emst/emst_main.cpp ++++ b/src/mlpack/methods/emst/emst_main.cpp +@@ -31,6 +31,10 @@ + #include "dtb.hpp" + + PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", ++ // Short description. ++ "An implementation of the Dual-Tree Boruvka algorithm for computing the " ++ "Euclidean minimum spanning tree of a set of input points.", ++ // Long description. + "This program can compute the Euclidean minimum spanning tree of a set of " + "input points using the dual-tree Boruvka algorithm." + "\n\n" +@@ -56,7 +60,14 @@ PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", + "The output matrix is a three-dimensional matrix, where each row indicates " + "an edge. The first dimension corresponds to the lesser index of the edge;" + " the second dimension corresponds to the greater index of the edge; and " +- "the third column corresponds to the distance between the two points."); ++ "the third column corresponds to the distance between the two points.", ++ SEE_ALSO("EMST Tutorial", "@doxygen/emst_tutorial.html"), ++ SEE_ALSO("Minimum spanning tree on Wikipedia", ++ "https://en.wikipedia.org/wiki/Minimum_spanning_tree"), ++ SEE_ALSO("Fast Euclidean Minimum Spanning Tree: Algorithm, Analysis, and " ++ "Applications (pdf)", "http://www.mlpack.org/papers/emst.pdf"), ++ SEE_ALSO("mlpack::emst::DualTreeBoruvka class documentation", ++ "@doxygen/classmlpack_1_1emst_1_1DualTreeBoruvka.html")); + + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); + PARAM_MATRIX_OUT("output", "Output data. Stored as an edge list.", "o"); +diff --git a/src/mlpack/methods/fastmks/CMakeLists.txt b/src/mlpack/methods/fastmks/CMakeLists.txt +index d9a6a6a8e..e6b6e55c1 100644 +--- a/src/mlpack/methods/fastmks/CMakeLists.txt ++++ b/src/mlpack/methods/fastmks/CMakeLists.txt +@@ -22,3 +22,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(fastmks) + add_python_binding(fastmks) ++add_markdown_docs(fastmks "cli;python" "geometry") +diff --git a/src/mlpack/methods/fastmks/fastmks_main.cpp b/src/mlpack/methods/fastmks/fastmks_main.cpp +index 1d8e7e7fb..fea273d65 100644 +--- a/src/mlpack/methods/fastmks/fastmks_main.cpp ++++ b/src/mlpack/methods/fastmks/fastmks_main.cpp +@@ -25,6 +25,12 @@ using namespace mlpack::metric; + using namespace mlpack::util; + + PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", ++ // Short description. ++ "An implementation of the single-tree and dual-tree fast max-kernel search" ++ " (FastMKS) algorithm. Given a set of reference points and a set of query" ++ " points, this can find the reference point with maximum kernel value for " ++ "each query point; trained models can be reused for future queries.", ++ // Long description. + "This program will find the k maximum kernels of a set of points, " + "using a query set and a reference set (which can optionally be the same " + "set). More specifically, for each point in the query set, the k points in" +@@ -51,7 +57,14 @@ PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", + "\n\n" + "This program performs FastMKS using a cover tree. The base used to build " + "the cover tree can be specified with the " + PRINT_PARAM_STRING("base") + +- " parameter."); ++ " parameter.", ++ SEE_ALSO("Fast max-kernel search tutorial (fastmks)", ++ "@doxygen/fmkstutorial.html"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Dual-tree Fast Exact Max-Kernel Search (pdf)", ++ "http://mlpack.org/papers/fmks.pdf"), ++ SEE_ALSO("mlpack::fastmks::FastMKS class documentation", ++ "@doxygen/classmlpack_1_1fastmks_1_1FastMKS.html")); + + // Model-building parameters. + PARAM_MATRIX_IN("reference", "The reference dataset.", "r"); +diff --git a/src/mlpack/methods/gmm/CMakeLists.txt b/src/mlpack/methods/gmm/CMakeLists.txt +index 74f9e8ace..964f490be 100644 +--- a/src/mlpack/methods/gmm/CMakeLists.txt ++++ b/src/mlpack/methods/gmm/CMakeLists.txt +@@ -23,7 +23,12 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(gmm_train) + add_python_binding(gmm_train) ++add_markdown_docs(gmm_train "cli;python" "clustering") ++ + add_cli_executable(gmm_generate) + add_python_binding(gmm_generate) ++add_markdown_docs(gmm_generate "cli;python" "clustering") ++ + add_cli_executable(gmm_probability) + add_python_binding(gmm_probability) ++add_markdown_docs(gmm_probability "cli;python" "clustering") +diff --git a/src/mlpack/methods/gmm/gmm_generate_main.cpp b/src/mlpack/methods/gmm/gmm_generate_main.cpp +index 784234941..4f3d10fdb 100644 +--- a/src/mlpack/methods/gmm/gmm_generate_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_generate_main.cpp +@@ -20,6 +20,10 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Sample Generator", ++ // Short description. ++ "A sample generator for pre-trained GMMs. Given a pre-trained GMM, this " ++ "can sample new points randomly from that distribution.", ++ // Long description. + "This program is able to generate samples from a pre-trained GMM (use " + "gmm_train to train a GMM). The pre-trained GMM must be specified with " + "the " + PRINT_PARAM_STRING("input_model") + " parameter. The number " +@@ -32,7 +36,13 @@ PROGRAM_INFO("GMM Sample Generator", + "samples in " + PRINT_DATASET("samples") + ":" + "\n\n" + + PRINT_CALL("gmm_generate", "input_model", "gmm", "samples", 100, "output", +- "samples")); ++ "samples"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM model to generate samples " + "from.", "m"); +diff --git a/src/mlpack/methods/gmm/gmm_probability_main.cpp b/src/mlpack/methods/gmm/gmm_probability_main.cpp +index f40c86628..6ba29585c 100644 +--- a/src/mlpack/methods/gmm/gmm_probability_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_probability_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Probability Calculator", ++ // Short description. ++ "A probability calculator for GMMs. Given a pre-trained GMM and a set of " ++ "points, this can compute the probability that each point is from the given" ++ " GMM.", ++ // Long description. + "This program calculates the probability that given points came from a " + "given GMM (that is, P(X | gmm)). The GMM is specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and the points are " +@@ -33,7 +38,13 @@ PROGRAM_INFO("GMM Probability Calculator", + PRINT_DATASET("probs") + ", the following command could be used:" + "\n\n" + + PRINT_CALL("gmm_probability", "input_model", "gmm", "input", "points", +- "output", "probs")); ++ "output", "probs"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM to use as model.", "m"); + PARAM_MATRIX_IN_REQ("input", "Input matrix to calculate probabilities of.", +diff --git a/src/mlpack/methods/gmm/gmm_train_main.cpp b/src/mlpack/methods/gmm/gmm_train_main.cpp +index c331d04dc..a01adfcb0 100644 +--- a/src/mlpack/methods/gmm/gmm_train_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_train_main.cpp +@@ -26,6 +26,11 @@ using namespace mlpack::kmeans; + using namespace std; + + PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", ++ // Short description. ++ "An implementation of the EM algorithm for training Gaussian mixture " ++ "models (GMMs). Given a dataset, this can train a GMM for future use " ++ "with other tools.", ++ // Long description. + "This program takes a parametric estimate of a Gaussian mixture model (GMM)" + " using the EM algorithm to find the maximum likelihood estimate. The " + "model may be saved and reused by other mlpack GMM tools." +@@ -84,7 +89,13 @@ PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", + ", the following command may be used: " + "\n\n" + + PRINT_CALL("gmm_train", "input_model", "gmm", "input", "data2", +- "gaussians", 6, "output_model", "new_gmm")); ++ "gaussians", 6, "output_model", "new_gmm"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + // Parameters for training. + PARAM_MATRIX_IN_REQ("input", "The training data on which the model will be " +diff --git a/src/mlpack/methods/hmm/CMakeLists.txt b/src/mlpack/methods/hmm/CMakeLists.txt +index 7add2baa3..7247430bb 100644 +--- a/src/mlpack/methods/hmm/CMakeLists.txt ++++ b/src/mlpack/methods/hmm/CMakeLists.txt +@@ -21,9 +21,16 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hmm_train) + add_python_binding(hmm_train) ++add_markdown_docs(hmm_train "cli;python" "misc. / other") ++ + add_cli_executable(hmm_loglik) + add_python_binding(hmm_loglik) ++add_markdown_docs(hmm_loglik "cli;python" "misc. / other") ++ + add_cli_executable(hmm_viterbi) + add_python_binding(hmm_viterbi) ++add_markdown_docs(hmm_viterbi "cli;python" "misc. / other") ++ + add_cli_executable(hmm_generate) + add_python_binding(hmm_generate) ++add_markdown_docs(hmm_generate "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/hmm/hmm_generate_main.cpp b/src/mlpack/methods/hmm/hmm_generate_main.cpp +index 0efc8dc84..cbfbe30bf 100644 +--- a/src/mlpack/methods/hmm/hmm_generate_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_generate_main.cpp +@@ -29,8 +29,13 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " +- "utility takes an already-trained HMM, specified as the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", ++ // Short description. ++ "A utility to generate random sequences from a pre-trained Hidden Markov " ++ "Model (HMM). The length of the desired sequence can be specified, and a " ++ "random sequence of observations is returned.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as the " + + PRINT_PARAM_STRING("model") + " parameter, and generates a random " + "observation sequence and hidden state sequence based on its parameters. " + "The observation sequence may be saved with the " + +@@ -47,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " + PRINT_DATASET("states") + ", the following command may be used: " + "\n\n" + + PRINT_CALL("hmm_generate", "model", "hmm", "length", 150, "output", +- "observations", "state", "states")); ++ "observations", "state", "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MODEL_IN_REQ(HMMModel, "model", "Trained HMM to generate sequences with.", + "m"); +diff --git a/src/mlpack/methods/hmm/hmm_loglik_main.cpp b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +index 6ebb3cbef..23a482603 100644 +--- a/src/mlpack/methods/hmm/hmm_loglik_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +@@ -26,8 +26,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " +- "utility takes an already-trained HMM, specified with the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", ++ // Short description. ++ "A utility for computing the log-likelihood of a sequence for Hidden Markov" ++ " Models (HMMs). Given a pre-trained HMM and an observation sequence, this" ++ " computes and returns the log-likelihood of that sequence being observed " ++ "from that HMM.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and evaluates the " + "log-likelihood of a sequence of observations, given with the " + + PRINT_PARAM_STRING("input") + " parameter. The computed log-likelihood is" +@@ -37,7 +43,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " + PRINT_DATASET("seq") + " with the pre-trained HMM " + PRINT_MODEL("hmm") + + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm")); ++ PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "File containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "File containing HMM.", "m"); +diff --git a/src/mlpack/methods/hmm/hmm_train_main.cpp b/src/mlpack/methods/hmm/hmm_train_main.cpp +index 109cfbddd..3e21085a1 100644 +--- a/src/mlpack/methods/hmm/hmm_train_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_train_main.cpp +@@ -27,9 +27,15 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " +- "Hidden Markov Model to be trained on labeled or unlabeled data. It " +- "support three types of HMMs: discrete HMMs, Gaussian HMMs, or GMM HMMs." ++PROGRAM_INFO("Hidden Markov Model (HMM) Training", ++ // Short description. ++ "An implementation of training algorithms for Hidden Markov Models (HMMs). " ++ "Given labeled or unlabeled data, an HMM can be trained for further use " ++ "with other mlpack HMM tools.", ++ // Long description. ++ "This program allows a Hidden Markov Model to be trained on labeled or " ++ "unlabeled data. It supports three types of HMMs: discrete HMMs, " ++ "Gaussian HMMs, or GMM HMMs." + "\n\n" + "Either one input sequence can be specified (with --input_file), or, a " + "file containing files in which input sequences can be found (when " +@@ -46,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " + "\n\n" + "Optionally, a pre-created HMM model can be used as a guess for the " + "transition matrix and emission probabilities; this is specifiable with " +- "--model_file."); ++ "--model_file.", ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_STRING_IN_REQ("input_file", "File containing input observations.", "i"); + PARAM_STRING_IN("type", "Type of HMM: discrete | gaussian | gmm.", "t", +diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +index d0e1168b3..1fa3c5f58 100644 +--- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +@@ -27,8 +27,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " +- "utility takes an already-trained HMM, specified as " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", ++ // Short description. ++ "A utility for computing the most probable hidden state sequence for Hidden" ++ " Markov Models (HMMs). Given a pre-trained HMM and an observed sequence, " ++ "this uses the Viterbi algorithm to compute and return the most probable " ++ "hidden state sequence.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as " + + PRINT_PARAM_STRING("input_model") + ", and evaluates the most probable " + "hidden state sequence of a given sequence of observations (specified as " + "'" + PRINT_PARAM_STRING("input") + ", using the Viterbi algorithm. The " +@@ -41,7 +47,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " + ", the following command could be used:" + "\n\n" + + PRINT_CALL("hmm_viterbi", "input", "obs", "input_model", "hmm", "output", +- "states")); ++ "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "Matrix containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "Trained HMM to use.", "m"); +diff --git a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +index b573a19c6..2a94415b3 100644 +--- a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt ++++ b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +@@ -30,3 +30,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hoeffding_tree) + add_python_binding(hoeffding_tree) ++add_markdown_docs(hoeffding_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +index 46437b4ad..45b58713c 100644 +--- a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp ++++ b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +@@ -26,6 +26,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Hoeffding trees", ++ // Short description. ++ "An implementation of Hoeffding trees, a form of streaming decision tree " ++ "for classification. Given labeled data, a Hoeffding tree can be trained " ++ "and saved for later use, or a pre-trained Hoeffding tree can be used for " ++ "predicting the classifications of new points.", ++ // Long description. + "This program implements Hoeffding trees, a form of streaming decision tree" + " suited best for large (or streaming) datasets. This program supports " + "both categorical and numeric data. Given an input dataset, this program " +@@ -70,7 +76,13 @@ PROGRAM_INFO("Hoeffding trees", + PRINT_DATASET("class_probs") + " with the following command: " + "\n\n" + + PRINT_CALL("hoeffding_tree", "input_model", "tree", "test", "test_set", +- "predictions", "predictions", "probabilities", "class_probs")); ++ "predictions", "predictions", "probabilities", "class_probs"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Mining High-Speed Data Streams (pdf)", ++ "http://dm.cs.washington.edu/papers/vfdt-kdd00.pdf"), ++ SEE_ALSO("mlpack::tree::HoeffdingTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1HoeffdingTree.html")); + + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", + "t"); +diff --git a/src/mlpack/methods/kernel_pca/CMakeLists.txt b/src/mlpack/methods/kernel_pca/CMakeLists.txt +index c14a9f3ec..9bd0cd3a0 100644 +--- a/src/mlpack/methods/kernel_pca/CMakeLists.txt ++++ b/src/mlpack/methods/kernel_pca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kernel_pca) + add_python_binding(kernel_pca) ++add_markdown_docs(kernel_pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +index 7ca626812..af6c5c17f 100644 +--- a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp ++++ b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +@@ -41,6 +41,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Kernel Principal Components Analysis", ++ // Short description. ++ "An implementation of Kernel Principal Components Analysis (KPCA). This " ++ "can be used to perform nonlinear dimensionality reduction or preprocessing" ++ " on a given dataset.", ++ // Long description. + "This program performs Kernel Principal Components Analysis (KPCA) on the " + "specified dataset with the specified kernel. This will transform the " + "data onto the kernel principal components, and optionally reduce the " +@@ -93,7 +98,13 @@ PROGRAM_INFO("Kernel Principal Components Analysis", + "the kernel matrix; to specify the sampling scheme, the " + + PRINT_PARAM_STRING("sampling") + " parameter is used. The " + "sampling scheme for the Nystr\u00F6m method can be chosen from the " +- "following list: 'kmeans', 'random', 'ordered'."); ++ "following list: 'kmeans', 'random', 'ordered'.", ++ SEE_ALSO("Kernel principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Kernel_principal_component_analysis"), ++ SEE_ALSO("Kernel Principal Component Analysis (pdf)", ++ "http://pca.narod.ru/scholkopf_kernel.pdf"), ++ SEE_ALSO("mlpack::kpca::KernelPCA class documentation", ++ "@doxygen/classmlpack_1_1kpca_1_1KernelPCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform KPCA on.", "i"); + PARAM_MATRIX_OUT("output", "Matrix to save modified dataset to.", "o"); +@@ -107,9 +118,9 @@ PARAM_INT_IN("new_dimensionality", "If not 0, reduce the dimensionality of " + PARAM_FLAG("center", "If set, the transformed data will be centered about the " + "origin.", "c"); + +-PARAM_FLAG("nystroem_method", "If set, the nystroem method will be used.", "n"); ++PARAM_FLAG("nystroem_method", "If set, the Nystroem method will be used.", "n"); + +-PARAM_STRING_IN("sampling", "Sampling scheme to use for the nystroem method: " ++PARAM_STRING_IN("sampling", "Sampling scheme to use for the Nystroem method: " + "'kmeans', 'random', 'ordered'", "s", "kmeans"); + + PARAM_DOUBLE_IN("kernel_scale", "Scale, for 'hyptan' kernel.", "S", 1.0); +diff --git a/src/mlpack/methods/kmeans/CMakeLists.txt b/src/mlpack/methods/kmeans/CMakeLists.txt +index bf4990083..9cfec0fdc 100644 +--- a/src/mlpack/methods/kmeans/CMakeLists.txt ++++ b/src/mlpack/methods/kmeans/CMakeLists.txt +@@ -40,3 +40,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kmeans) + add_python_binding(kmeans) ++add_markdown_docs(kmeans "cli;python" "clustering") +diff --git a/src/mlpack/methods/kmeans/kmeans_main.cpp b/src/mlpack/methods/kmeans/kmeans_main.cpp +index b0319ccb5..9d3f40c7d 100644 +--- a/src/mlpack/methods/kmeans/kmeans_main.cpp ++++ b/src/mlpack/methods/kmeans/kmeans_main.cpp +@@ -28,11 +28,17 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " +- "on the given dataset. It can return the learned cluster assignments, and " +- "the centroids of the clusters. Empty clusters are not allowed by default;" +- " when a cluster becomes empty, the point furthest from the centroid of the" +- " cluster with maximum variance is taken to fill that cluster." ++PROGRAM_INFO("K-Means Clustering", ++ // Short description. ++ "An implementation of several strategies for efficient k-means clustering. " ++ "Given a dataset and a value of k, this computes and returns a k-means " ++ "clustering on that data.", ++ // Long description. ++ "This program performs K-Means clustering on the given dataset. It can " ++ "return the learned cluster assignments, and the centroids of the clusters." ++ " Empty clusters are not allowed by default; when a cluster becomes empty," ++ " the point furthest from the centroid of the cluster with maximum variance" ++ " is taken to fill that cluster." + "\n\n" + "Optionally, the Bradley and Fayyad approach (\"Refining initial points for" + " k-means clustering\", 1998) can be used to select initial points by " +@@ -85,7 +91,21 @@ PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " + "following command may be used:" + "\n\n" + + PRINT_CALL("kmeans", "input", "data", "initial_centroids", "initial", +- "clusters", 10, "max_iterations", 500, "centroid", "final")); ++ "clusters", 10, "max_iterations", 500, "centroid", "final"), ++ SEE_ALSO("K-Means tutorial", "@doxygen/kmtutorial.html"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Using the triangle inequality to accelerate k-means (pdf)", ++ "http://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf"), ++ SEE_ALSO("Making k-means even faster (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.586.2554" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("Accelerating exact k-means algorithms with geometric reasoning " ++ "(pdf)", "http://reports-archive.adm.cs.cmu.edu/anon/anon/usr/ftp/" ++ "usr0/ftp/2000/CMU-CS-00-105.pdf"), ++ SEE_ALSO("A dual-tree algorithm for fast k-means clustering with large k " ++ "(pdf)", "http://www.ratml.org/pub/pdf/2017dual.pdf"), ++ SEE_ALSO("mlpack::kmeans::KMeans class documentation", ++ "@doxygen/classmlpack_1_1kmeans_1_1KMeans.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/lars/CMakeLists.txt b/src/mlpack/methods/lars/CMakeLists.txt +index 1388f24be..7e4cb7890 100644 +--- a/src/mlpack/methods/lars/CMakeLists.txt ++++ b/src/mlpack/methods/lars/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(lars) + add_python_binding(lars) ++add_markdown_docs(lars "cli;python" "regression") +diff --git a/src/mlpack/methods/lars/lars_main.cpp b/src/mlpack/methods/lars/lars_main.cpp +index 040c5f0c3..e0b2f38f3 100644 +--- a/src/mlpack/methods/lars/lars_main.cpp ++++ b/src/mlpack/methods/lars/lars_main.cpp +@@ -21,10 +21,16 @@ using namespace mlpack; + using namespace mlpack::regression; + using namespace mlpack::util; + +-PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " +- "(Stagewise/laSso). This is a stage-wise homotopy-based algorithm for " +- "L1-regularized linear regression (LASSO) and L1+L2-regularized linear " +- "regression (Elastic Net)." ++PROGRAM_INFO("LARS", ++ // Short description. ++ "An implementation of Least Angle Regression (Stagewise/laSso), also known" ++ " as LARS. This can train a LARS/LASSO/Elastic Net model and use that " ++ "model or a pre-trained model to output regression predictions for a test " ++ "set.", ++ // Long description. ++ "An implementation of LARS: Least Angle Regression (Stagewise/laSso). " ++ "This is a stage-wise homotopy-based algorithm for L1-regularized linear " ++ "regression (LASSO) and L1+L2-regularized linear regression (Elastic Net)." + "\n\n" + "This program is able to train a LARS/LASSO/Elastic Net model or load a " + "model from file, output regression predictions for a test set, and save " +@@ -80,7 +86,12 @@ PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " + "and save those responses to " + PRINT_DATASET("test_predictions") + ": " + "\n\n" + + PRINT_CALL("lars", "input_model", "lasso_model", "test", "test", +- "output_predictions", "test_predictions")); ++ "output_predictions", "test_predictions"), ++ SEE_ALSO("@linear_regression", "#linear_regression"), ++ SEE_ALSO("Least angle regression (pdf)", ++ "http://mlpack.org/papers/lars.pdf"), ++ SEE_ALSO("mlpack::regression::LARS C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LARS.html")); + + PARAM_TMATRIX_IN("input", "Matrix of covariates (X).", "i"); + PARAM_MATRIX_IN("responses", "Matrix of responses/observations (y).", "r"); +diff --git a/src/mlpack/methods/linear_regression/CMakeLists.txt b/src/mlpack/methods/linear_regression/CMakeLists.txt +index 58a7426e8..b4e63cdca 100644 +--- a/src/mlpack/methods/linear_regression/CMakeLists.txt ++++ b/src/mlpack/methods/linear_regression/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(linear_regression) + add_python_binding(linear_regression) ++add_markdown_docs(linear_regression "cli;python" "regression") +diff --git a/src/mlpack/methods/linear_regression/linear_regression_main.cpp b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +index c1e33cd06..1cb58a85c 100644 +--- a/src/mlpack/methods/linear_regression/linear_regression_main.cpp ++++ b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +@@ -22,6 +22,12 @@ using namespace arma; + using namespace std; + + PROGRAM_INFO("Simple Linear Regression and Prediction", ++ // Short description. ++ "An implementation of simple linear regression and ridge regression using " ++ "ordinary least squares. Given a dataset and responses, a model can be " ++ "trained and saved for later use, or a pre-trained model can be used to " ++ "output regression predictions for a test set.", ++ // Long description. + "An implementation of simple linear regression and simple ridge regression " + "using ordinary least squares. This solves the problem" + "\n\n" +@@ -63,7 +69,13 @@ PROGRAM_INFO("Simple Linear Regression and Prediction", + "used:" + "\n\n" + + PRINT_CALL("linear_regression", "input_model", "lr_model", "test", "X_test", +- "output_predictions", "X_test_responses")); ++ "output_predictions", "X_test_responses"), ++ SEE_ALSO("Linear/ridge regression tutorial", "@doxygen/lrtutorial.html"), ++ SEE_ALSO("@lars", "#lars"), ++ SEE_ALSO("Linear regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Linear_regression"), ++ SEE_ALSO("mlpack::regression::LinearRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LinearRegression.html")); + + PARAM_MATRIX_IN("training", "Matrix containing training set X (regressors).", + "t"); +diff --git a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +index 6720525c7..d4b498e55 100644 +--- a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt ++++ b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(local_coordinate_coding) + add_python_binding(local_coordinate_coding) ++add_markdown_docs(local_coordinate_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +index f0ee7e6dc..16d8e13d8 100644 +--- a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp ++++ b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +@@ -24,6 +24,12 @@ using namespace mlpack::sparse_coding; // For NothingInitializer. + using namespace mlpack::util; + + PROGRAM_INFO("Local Coordinate Coding", ++ // Short description. ++ "An implementation of Local Coordinate Coding (LCC), a data transformation " ++ "technique. Given input data, this transforms each point to be expressed " ++ "as a linear combination of a few points in the dataset; once an LCC model " ++ "is trained, it can be used to transform points later also.", ++ // Long description. + "An implementation of Local Coordinate Coding (LCC), which " + "codes data that approximately lives on a manifold using a variation of l1-" + "norm regularized sparse coding. Given a dense data matrix X with n points" +@@ -67,7 +73,13 @@ PROGRAM_INFO("Local Coordinate Coding", + "be used:" + "\n\n" + + PRINT_CALL("local_coordinate_coding", "input_model", "lcc_model", "test", +- "points", "codes", "new_codes")); ++ "points", "codes", "new_codes"), ++ SEE_ALSO("@sparse_coding", "#sparse_coding"), ++ SEE_ALSO("Nonlinear learning using local coordinate coding (pdf)", ++ "https://papers.nips.cc/paper/3875-nonlinear-learning-using-local-" ++ "coordinate-coding.pdf"), ++ SEE_ALSO("mlpack::lcc::LocalCoordinateCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1lcc_1_1LocalCoordinateCoding.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +diff --git a/src/mlpack/methods/logistic_regression/CMakeLists.txt b/src/mlpack/methods/logistic_regression/CMakeLists.txt +index d7edd6ba1..673e214c6 100644 +--- a/src/mlpack/methods/logistic_regression/CMakeLists.txt ++++ b/src/mlpack/methods/logistic_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(logistic_regression) + add_python_binding(logistic_regression) ++add_markdown_docs(logistic_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +index 2a0ed23a7..385525ab0 100644 +--- a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp ++++ b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +@@ -24,6 +24,11 @@ using namespace mlpack::optimization; + using namespace mlpack::util; + + PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", ++ // Short description. ++ "An implementation of L2-regularized logistic regression for two-class " ++ "classification. Given labeled data, a model can be trained and saved for " ++ "future use; or, a pre-trained model can be used to classify new points.", ++ // Long description. + "An implementation of L2-regularized logistic regression using either the " + "L-BFGS optimizer or SGD (stochastic gradient descent). This solves the " + "regression problem" +@@ -39,8 +44,8 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + "those things at once. In addition, this program allows classification on " + "a test dataset (specified with the " + PRINT_PARAM_STRING("test") + " " + "parameter) and the classification results may be saved with the " + +- PRINT_PARAM_STRING("output") + " output parameter. The trained logistic " +- "regression model may be saved using the " + ++ PRINT_PARAM_STRING("output") + " output parameter." ++ " The trained logistic regression model may be saved using the " + + PRINT_PARAM_STRING("output_model") + " output parameter." + "\n\n" + "The training data, if specified, may have class labels as its last " +@@ -95,7 +100,13 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + PRINT_DATASET("predictions") + "', the following command may be used: " + "\n\n" + + PRINT_CALL("logistic_regression", "input_model", "lr_model", "test", "test", +- "output", "predictions")); ++ "output", "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Logistic regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Logistic_regression"), ++ SEE_ALSO("mlpack::regression::LogisticRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LogisticRegression.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/lsh/CMakeLists.txt b/src/mlpack/methods/lsh/CMakeLists.txt +index 758a2152b..4ec7baacf 100644 +--- a/src/mlpack/methods/lsh/CMakeLists.txt ++++ b/src/mlpack/methods/lsh/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # sets with p-stable LSH. + add_cli_executable(lsh) + add_python_binding(lsh) ++add_markdown_docs(lsh "cli;python" "geometry") +diff --git a/src/mlpack/methods/lsh/lsh_main.cpp b/src/mlpack/methods/lsh/lsh_main.cpp +index fdf34d3cf..840fc43e4 100644 +--- a/src/mlpack/methods/lsh/lsh_main.cpp ++++ b/src/mlpack/methods/lsh/lsh_main.cpp +@@ -25,6 +25,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", ++ // Short description. ++ "An implementation of approximate k-nearest-neighbor search with " ++ "locality-sensitive hashing (LSH). Given a set of reference points and a " ++ "set of query points, this will compute the k approximate nearest neighbors" ++ " of each query point in the reference set; models can be saved for future " ++ "use.", ++ // Long description. + "This program will calculate the k approximate-nearest-neighbors of a set " + "of points using locality-sensitive hashing. You may specify a separate set" + " of reference points and query points, or just a reference set which will " +@@ -49,7 +56,15 @@ PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", + " parameter can be specified to set the random seed." + "\n\n" + "This program also has many other parameters to control its functionality;" +- " see the parameter-specific documentation for more information."); ++ " see the parameter-specific documentation for more information.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("Locality-sensitive hashing on Wikipedia", ++ "https://en.wikipedia.org/wiki/Locality-sensitive_hashing"), ++ SEE_ALSO("Locality-sensitive hashing scheme based on p-stable distributions" ++ " (pdf)", "http://mlpack.org/papers/lsh.pdf"), ++ SEE_ALSO("mlpack::neighbor::LSHSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1LSHSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/mean_shift/CMakeLists.txt b/src/mlpack/methods/mean_shift/CMakeLists.txt +index 1087ab532..5aced5225 100644 +--- a/src/mlpack/methods/mean_shift/CMakeLists.txt ++++ b/src/mlpack/methods/mean_shift/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(mean_shift) + add_python_binding(mean_shift) ++add_markdown_docs(mean_shift "cli;python" "clustering") +diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +index 4710ccaad..ac7f17e18 100644 +--- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp ++++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +@@ -23,9 +23,15 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " +- "clustering on the given dataset, storing the learned cluster assignments " +- "either as a column of labels in the input dataset or separately." ++PROGRAM_INFO("Mean Shift Clustering", ++ // Short description. ++ "A fast implementation of mean-shift clustering using dual-tree range " ++ "search. Given a dataset, this uses the mean shift algorithm to produce " ++ "and return a clustering of the data.", ++ // Long description. ++ "This program performs mean shift clustering on the given dataset, storing " ++ "the learned cluster assignments either as a column of labels in the input " ++ "dataset or separately." + "\n\n" + "The input dataset should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter, and the radius used for search" +@@ -42,7 +48,16 @@ PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " + PRINT_DATASET("data") + " and store the centroids to " + + PRINT_DATASET("centroids") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids")); ++ PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids"), ++ SEE_ALSO("@kmeans", "#kmeans"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Mean shift on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mean_shift"), ++ SEE_ALSO("Mean Shift, Mode Seeking, and Clustering (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.510.1222" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::mean_shift::MeanShift C++ class documentation", ++ "@doxygen/classmlpack_1_1meanshift_1_1MeanShift.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/naive_bayes/CMakeLists.txt b/src/mlpack/methods/naive_bayes/CMakeLists.txt +index f15dc229c..b2afe0707 100644 +--- a/src/mlpack/methods/naive_bayes/CMakeLists.txt ++++ b/src/mlpack/methods/naive_bayes/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nbc) + add_python_binding(nbc) ++add_markdown_docs(nbc "cli;python" "classification") +diff --git a/src/mlpack/methods/naive_bayes/nbc_main.cpp b/src/mlpack/methods/naive_bayes/nbc_main.cpp +index 68a4a70e0..612d675d0 100644 +--- a/src/mlpack/methods/naive_bayes/nbc_main.cpp ++++ b/src/mlpack/methods/naive_bayes/nbc_main.cpp +@@ -26,6 +26,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Parametric Naive Bayes Classifier", ++ // Short description. ++ "An implementation of the Naive Bayes Classifier, used for classification. " ++ "Given labeled data, an NBC model can be trained and saved, or, a " ++ "pre-trained model can be used for classification.", ++ // Long description. + "This program trains the Naive Bayes classifier on the given labeled " + "training set, or loads a model from the given model file, and then may use" + " that trained model to classify the points in a given test set." +@@ -66,7 +71,14 @@ PROGRAM_INFO("Parametric Naive Bayes Classifier", + "may be used:" + "\n\n" + + PRINT_CALL("nbc", "input_model", "nbc_model", "test", "test_set", "output", +- "predictions")); ++ "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Naive Bayes classifier on Wikipedia", ++ "https://en.wikipedia.org/wiki/Naive_Bayes_classifier"), ++ SEE_ALSO("mlpack::naive_bayes::NaiveBayesClassifier C++ class " ++ "documentation", "@doxygen/classmlpack_1_1naive__bayes_1_1" ++ "NaiveBayesClassifier.html")); + + // A struct for saving the model with mappings. + struct NBCModel +diff --git a/src/mlpack/methods/nca/CMakeLists.txt b/src/mlpack/methods/nca/CMakeLists.txt +index 777eef39b..7b1fd98f5 100644 +--- a/src/mlpack/methods/nca/CMakeLists.txt ++++ b/src/mlpack/methods/nca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nca) + add_python_binding(nca) ++add_markdown_docs(nca "cli;python" "transformations") +diff --git a/src/mlpack/methods/nca/nca_main.cpp b/src/mlpack/methods/nca/nca_main.cpp +index cc988f128..44e9d4525 100644 +--- a/src/mlpack/methods/nca/nca_main.cpp ++++ b/src/mlpack/methods/nca/nca_main.cpp +@@ -22,6 +22,12 @@ + + // Define parameters. + PROGRAM_INFO("Neighborhood Components Analysis (NCA)", ++ // Short description. ++ "An implementation of neighborhood components analysis, a distance learning" ++ " technique that can be used for preprocessing. Given a labeled dataset, " ++ "this uses NCA, which seeks to improve the k-nearest-neighbor " ++ "classification, and returns the learned distance metric.", ++ // Long description. + "This program implements Neighborhood Components Analysis, both a linear " + "dimensionality reduction technique and a distance learning technique. The" + " method seeks to improve k-nearest-neighbor classification on a dataset " +@@ -82,7 +88,14 @@ PROGRAM_INFO("Neighborhood Components Analysis (NCA)", + "mlpack L-BFGS documentation (in lbfgs.hpp) or the vast set of published " + "literature on L-BFGS." + "\n\n" +- "By default, the SGD optimizer is used."); ++ "By default, the SGD optimizer is used.", ++ SEE_ALSO("Neighbourhood components analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Neighbourhood_components_analysis"), ++ SEE_ALSO("Neighbourhood components analysis (pdf)", ++ "http://papers.nips.cc/paper/2566-neighbourhood-components-" ++ "analysis.pdf"), ++ SEE_ALSO("mlpack::nca::NCA C++ class documentation", ++ "@doxygen/classmlpack_1_1nca_1_1NCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to run NCA on.", "i"); + PARAM_MATRIX_OUT("output", "Output matrix for learned distance matrix.", "o"); +diff --git a/src/mlpack/methods/neighbor_search/CMakeLists.txt b/src/mlpack/methods/neighbor_search/CMakeLists.txt +index 1958ebe17..789beb295 100644 +--- a/src/mlpack/methods/neighbor_search/CMakeLists.txt ++++ b/src/mlpack/methods/neighbor_search/CMakeLists.txt +@@ -29,5 +29,8 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # Add mlpack_knn and mlpack_kfn executables. + add_cli_executable(knn) + add_python_binding(knn) ++add_markdown_docs(knn "cli;python" "geometry") ++ + add_cli_executable(kfn) + add_python_binding(kfn) ++add_markdown_docs(kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp +index 56415c3ae..57e2ffdf6 100644 +--- a/src/mlpack/methods/neighbor_search/kfn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp +@@ -34,6 +34,12 @@ typedef NSModel KFNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Furthest-Neighbors Search", ++ // Short description. ++ "An implementation of k-furthest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k furthest neighbors in the reference set of each query" ++ " point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-furthest-neighbors of a set of " + "points. You may specify a separate set of reference points and query " + "points, or just a reference set which will be used as both the reference " +@@ -51,7 +57,13 @@ PROGRAM_INFO("k-Furthest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th furthest neighbor from the point in the " + "query set with index i. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@approx_kfn", "#approx_kfn"), ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp +index 336315dea..46a2b53bf 100644 +--- a/src/mlpack/methods/neighbor_search/knn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/knn_main.cpp +@@ -36,6 +36,12 @@ typedef NSModel KNNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Nearest-Neighbors Search", ++ // Short description. ++ "An implementation of k-nearest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k nearest neighbors in the reference set of each query " ++ "point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-nearest-neighbors of a set of " + "points using kd-trees or cover trees (cover tree support is experimental " + "and may be slow). You may specify a separate set of " +@@ -53,7 +59,16 @@ PROGRAM_INFO("k-Nearest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th nearest neighbor from the point in the " + "query set with index i. Row j and column i in the distances output matrix" +- " corresponds to the distance between those two points."); ++ " corresponds to the distance between those two points.", ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("@kfn", "#kfn"), ++ SEE_ALSO("NeighborSearch tutorial (k-nearest-neighbors)", ++ "@doxygen/nstutorial.html"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/nmf/CMakeLists.txt b/src/mlpack/methods/nmf/CMakeLists.txt +index 18e01f1b4..f1c4666f3 100644 +--- a/src/mlpack/methods/nmf/CMakeLists.txt ++++ b/src/mlpack/methods/nmf/CMakeLists.txt +@@ -1,2 +1,3 @@ + add_cli_executable(nmf) + add_python_binding(nmf) ++add_markdown_docs(nmf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/nmf/nmf_main.cpp b/src/mlpack/methods/nmf/nmf_main.cpp +index e39273eb2..1e9e4a539 100644 +--- a/src/mlpack/methods/nmf/nmf_main.cpp ++++ b/src/mlpack/methods/nmf/nmf_main.cpp +@@ -28,10 +28,15 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " +- "non-negative matrix factorization on the given dataset, storing the " +- "resulting decomposed matrices in the specified files. For an input " +- "dataset V, NMF decomposes V into two matrices W and H such that " ++PROGRAM_INFO("Non-negative Matrix Factorization", ++ // Short description. ++ "An implementation of non-negative matrix factorization. This can be used " ++ "to decompose an input dataset into two low-rank non-negative components.", ++ // Long description. ++ "This program performs non-negative matrix factorization on the given " ++ "dataset, storing the resulting decomposed matrices in the specified " ++ "files. For an input dataset V, NMF decomposes V into two matrices W " ++ "and H such that " + "\n\n" + "V = W * H" + "\n\n" +@@ -60,7 +65,17 @@ PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " + PRINT_DATASET("H") + ", the following command could be used: " + "\n\n" + + PRINT_CALL("nmf", "input", "V", "w", "W", "h", "H", "rank", 10, +- "update_rules", "multdist")); ++ "update_rules", "multdist"), ++ SEE_ALSO("@cf", "#cf"), ++ SEE_ALSO("Alternating matrix factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Non-negative matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Non-negative_matrix_factorization"), ++ SEE_ALSO("Algorithms for non-negative matrix factorization (pdf)", ++ "http://papers.nips.cc/paper/1861-algorithms-for-non-negative-matrix-" ++ "factorization.pdf"), ++ SEE_ALSO("mlpack::amf::AMF C++ class documentation", ++ "@doxygen/classmlpack_1_1amf_1_1AMF.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform NMF on.", "i"); +diff --git a/src/mlpack/methods/pca/CMakeLists.txt b/src/mlpack/methods/pca/CMakeLists.txt +index 097598ee8..8b317c4fc 100644 +--- a/src/mlpack/methods/pca/CMakeLists.txt ++++ b/src/mlpack/methods/pca/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(pca) + add_python_binding(pca) ++add_markdown_docs(pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/pca/pca_main.cpp b/src/mlpack/methods/pca/pca_main.cpp +index 4176f0680..493a80026 100644 +--- a/src/mlpack/methods/pca/pca_main.cpp ++++ b/src/mlpack/methods/pca/pca_main.cpp +@@ -26,12 +26,18 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Principal Components Analysis", "This program performs principal " +- "components analysis on the given dataset using the exact, randomized, " +- "randomized block Krylov, or QUIC SVD method. It will transform the data " +- "onto its principal components, optionally performing dimensionality " +- "reduction by ignoring the principal components with the smallest " +- "eigenvalues." ++PROGRAM_INFO("Principal Components Analysis", ++ // Short description. ++ "An implementation of several strategies for principal components analysis " ++ "(PCA), a common preprocessing step. Given a dataset and a desired new " ++ "dimensionality, this can reduce the dimensionality of the data using the " ++ "linear transformation determined by PCA.", ++ // Long description. ++ "This program performs principal components analysis on the given dataset " ++ "using the exact, randomized, randomized block Krylov, or QUIC SVD method. " ++ "It will transform the data onto its principal components, optionally " ++ "performing dimensionality reduction by ignoring the principal components " ++ "with the smallest eigenvalues." + "\n\n" + "Use the " + PRINT_PARAM_STRING("input") + " parameter to specify the " + "dataset to perform PCA on. A desired new dimensionality can be specified " +@@ -52,7 +58,11 @@ PROGRAM_INFO("Principal Components Analysis", "This program performs principal " + PRINT_DATASET("data_mod") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("pca", "input", "data", "new_dimensionality", 5, +- "decomposition_method", "randomized", "output", "data_mod")); ++ "decomposition_method", "randomized", "output", "data_mod"), ++ SEE_ALSO("Principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Principal_component_analysis"), ++ SEE_ALSO("mlpack::pca::PCA C++ class documentation", ++ "@doxygen/classmlpack_1_1pca_1_1PCA.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform PCA on.", "i"); +diff --git a/src/mlpack/methods/perceptron/CMakeLists.txt b/src/mlpack/methods/perceptron/CMakeLists.txt +index f0b4afc59..96a003562 100644 +--- a/src/mlpack/methods/perceptron/CMakeLists.txt ++++ b/src/mlpack/methods/perceptron/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(perceptron) + add_python_binding(perceptron) ++add_markdown_docs(perceptron "cli;python" "classification") +diff --git a/src/mlpack/methods/perceptron/perceptron_main.cpp b/src/mlpack/methods/perceptron/perceptron_main.cpp +index 524a758ab..1127e9058 100644 +--- a/src/mlpack/methods/perceptron/perceptron_main.cpp ++++ b/src/mlpack/methods/perceptron/perceptron_main.cpp +@@ -26,6 +26,12 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Perceptron", ++ // Short description. ++ "An implementation of a perceptron---a single level neural network--=for " ++ "classification. Given labeled data, a perceptron can be trained and saved" ++ " for future use; or, a pre-trained perceptron can be used for " ++ "classification on new points.", ++ // Long description. + "This program implements a perceptron, which is a single level neural " + "network. The perceptron makes its predictions based on a linear predictor " + "function combining a set of weights with the feature vector. The " +@@ -75,7 +81,12 @@ PROGRAM_INFO("Perceptron", + "cannot pass a perceptron model trained on 2 classes and then re-train with" + " a 4-class dataset. Similarly, attempting classification on a " + "3-dimensional dataset with a perceptron that has been trained on 8 " +- "dimensions will cause an error."); ++ "dimensions will cause an error.", ++ SEE_ALSO("@adaboost", "#adaboost"), ++ SEE_ALSO("Perceptron on Wikipedia", ++ "https://en.wikipedia.org/wiki/Perceptron"), ++ SEE_ALSO("mlpack::perceptron::Perceptron C++ class documentation", ++ "@doxygen/classmlpack_1_1perceptron_1_1Perceptron.html")); + + // When we save a model, we must also save the class mappings. So we use this + // auxiliary structure to store both the perceptron and the mapping, and we'll +diff --git a/src/mlpack/methods/preprocess/CMakeLists.txt b/src/mlpack/methods/preprocess/CMakeLists.txt +index af9299c64..a65487981 100644 +--- a/src/mlpack/methods/preprocess/CMakeLists.txt ++++ b/src/mlpack/methods/preprocess/CMakeLists.txt +@@ -15,10 +15,17 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + #add_cli_executable(preprocess_stats) + add_cli_executable(preprocess_split) + add_python_binding(preprocess_split) ++add_markdown_docs(preprocess_split "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_binarize) + add_python_binding(preprocess_binarize) ++add_markdown_docs(preprocess_binarize "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_describe) + add_python_binding(preprocess_describe) ++add_markdown_docs(preprocess_describe "cli;python" "preprocessing") ++ + #add_cli_executable(preprocess_scan) + add_cli_executable(preprocess_imputer) + #add_python_binding(preprocess_imputer) ++add_markdown_docs(preprocess_imputer "cli" "preprocessing") +diff --git a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +index 53e6fb056..9d69834af 100644 +--- a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +@@ -14,7 +14,13 @@ + #include + #include + +-PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " ++PROGRAM_INFO("Binarize Data", ++ // Short description. ++ "A utility to binarize a dataset. Given a dataset, this utility converts " ++ "each value in the desired dimension(s) to 0 or 1; this can be a useful " ++ "preprocessing step.", ++ // Long description. ++ "This utility takes a dataset and binarizes the " + "variables into either 0 or 1 given threshold. User can apply binarization " + "on a dimension or the whole dataset. The dimension to apply binarization " + "to can be specified using the " + PRINT_PARAM_STRING("dimension") + +@@ -38,7 +44,10 @@ PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " + PRINT_DATASET("X") + ", we could instead run" + "\n\n" + + PRINT_CALL("preprocess_binarize", "input", "X", "threshold", 5.0, +- "dimension", 0, "output", "Y")); ++ "dimension", 0, "output", "Y"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +index 744f826a5..43b6df7f5 100644 +--- a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +@@ -22,12 +22,17 @@ using namespace mlpack::util; + using namespace std; + using namespace boost; + +-PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " +- "prints out the descriptive statistics of the data. Descriptive statistics " +- "is the discipline of quantitatively describing the main features of a " +- "collection of information, or the quantitative description itself. The " +- "program does not modify the original file, but instead prints out the " +- "statistics to the console. The printed result will look like a table." ++PROGRAM_INFO("Descriptive Statistics", ++ // Short description. ++ "A utility for printing descriptive statistics about a dataset. This " ++ "prints a number of details about a dataset in a tabular format.", ++ // Long description. ++ "This utility takes a dataset and prints out the descriptive statistics " ++ "of the data. Descriptive statistics is the discipline of quantitatively " ++ "describing the main features of a collection of information, or the " ++ "quantitative description itself. The program does not modify the original " ++ "file, but instead prints out the statistics to the console. The printed " ++ "result will look like a table." + "\n\n" + "Optionally, width and precision of the output can be adjusted by a user " + "using the " + PRINT_PARAM_STRING("width") + " and " + +@@ -47,7 +52,10 @@ PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " + "the dataset as a population, we could run" + "\n\n" + + PRINT_CALL("preprocess_describe", "input", "X", "width", 10, "precision", 5, +- "verbose", true)); ++ "verbose", true), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data,", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +index 848bbe9cf..1825334ad 100644 +--- a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +@@ -23,8 +23,14 @@ + #include + #include + +-PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " +- "defined missing variable to another to provide more meaningful analysis " ++PROGRAM_INFO("Impute Data", ++ // Short description. ++ "This utility provides several imputation strategies for missing data. " ++ "Given a dataset with missing values, this can impute according to several " ++ "strategies, including user-defined values.", ++ // Long description. ++ "This utility takes a dataset and converts a user-defined missing variable " ++ "to another to provide more meaningful analysis." + "\n\n" + "The program does not modify the original file, but instead makes a " + "separate file to save the output data; You can save the output by " +@@ -35,7 +41,10 @@ PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " + "column-wise dataset, and save the result to result.csv, we could run" + "\n\n" + "$ mlpack_preprocess_imputer -i dataset.csv -o result.csv -m NULL -d 0 \n" +- "> -s listwise_deletion"); ++ "> -s listwise_deletion", ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + PARAM_STRING_IN_REQ("input_file", "File containing data.", "i"); + PARAM_STRING_OUT("output_file", "File to save output into.", "o"); +diff --git a/src/mlpack/methods/preprocess/preprocess_split_main.cpp b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +index a66056012..5fafd05c3 100644 +--- a/src/mlpack/methods/preprocess/preprocess_split_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +@@ -15,11 +15,16 @@ + #include + #include + +-PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " +- "and splits them into a training set and a test set. Before the split, the " +- "points in the dataset are randomly reordered. The percentage of the " +- "dataset to be used as the test set can be specified with the " + +- PRINT_PARAM_STRING("test_ratio") + " parameter; the default is 0.2 (20%)." ++PROGRAM_INFO("Split Data", ++ // Short description. ++ "A utility to split data into a training and testing dataset. This can " ++ "also split labels according to the same split.", ++ // Long description. ++ "This utility takes a dataset and optionally labels and splits them into a " ++ "training set and a test set. Before the split, the points in the dataset " ++ "are randomly reordered. The percentage of the dataset to be used as the " ++ "test set can be specified with the " + PRINT_PARAM_STRING("test_ratio") + ++ " parameter; the default is 0.2 (20%)." + "\n\n" + "The output training and test matrices may be saved with the " + + PRINT_PARAM_STRING("training") + " and " + PRINT_PARAM_STRING("test") + +@@ -48,7 +53,10 @@ PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " + "\n\n" + + PRINT_CALL("preprocess_split", "input", "X", "input_labels", "y", + "test_ratio", 0.3, "training", "X_train", "training_labels", "y_train", +- "test", "X_test", "test_labels", "y_test")); ++ "test", "X_test", "test_labels", "y_test"), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data.", "i"); +diff --git a/src/mlpack/methods/radical/CMakeLists.txt b/src/mlpack/methods/radical/CMakeLists.txt +index 886566b11..c7dc29cac 100644 +--- a/src/mlpack/methods/radical/CMakeLists.txt ++++ b/src/mlpack/methods/radical/CMakeLists.txt +@@ -15,3 +15,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(radical) + add_python_binding(radical) ++add_markdown_docs(radical "cli;python" "transformations") +diff --git a/src/mlpack/methods/radical/radical_main.cpp b/src/mlpack/methods/radical/radical_main.cpp +index 7d5e59447..2de949635 100644 +--- a/src/mlpack/methods/radical/radical_main.cpp ++++ b/src/mlpack/methods/radical/radical_main.cpp +@@ -15,11 +15,18 @@ + #include + #include "radical.hpp" + +-PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" +- "component analysis (ICA). Assuming that we have an input matrix X, the" +- "goal is to find a square unmixing matrix W such that Y = W * X and the " +- "dimensions of Y are independent components. If the algorithm is running" +- "particularly slowly, try reducing the number of replicates." ++PROGRAM_INFO("RADICAL", ++ // Short description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Given a dataset, this can decompose the dataset into an unmixing " ++ "matrix and an independent component matrix; this can be useful for " ++ "preprocessing.", ++ // Long description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Assuming that we have an input matrix X, the goal is to find a " ++ "square unmixing matrix W such that Y = W * X and the dimensions of Y are " ++ "independent components. If the algorithm is running particularly slowly, " ++ "try reducing the number of replicates." + "\n\n" + "The input matrix to perform ICA on should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter. The output matrix Y may be " +@@ -31,7 +38,14 @@ PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" + "40 replicates, saving the independent components to " + + PRINT_DATASET("ic") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic")); ++ PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic"), ++ SEE_ALSO("Independent component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Independent_component_analysis"), ++ SEE_ALSO("ICA using spacings estimates of entropy (pdf)", ++ "http://www.jmlr.org/papers/volume4/learned-miller03a/" ++ "learned-miller03a.pdf"), ++ SEE_ALSO("mlpack::radical::Radical C++ class documentation", ++ "@doxygen/classmlpack_1_1radical_1_1Radical.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset for ICA.", "i"); + +diff --git a/src/mlpack/methods/random_forest/CMakeLists.txt b/src/mlpack/methods/random_forest/CMakeLists.txt +index 3ac70de8a..89b03837d 100644 +--- a/src/mlpack/methods/random_forest/CMakeLists.txt ++++ b/src/mlpack/methods/random_forest/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(random_forest) + add_python_binding(random_forest) ++add_markdown_docs(random_forest "cli;python" "classification") +diff --git a/src/mlpack/methods/random_forest/random_forest_main.cpp b/src/mlpack/methods/random_forest/random_forest_main.cpp +index ed22222cf..d0916ed6b 100644 +--- a/src/mlpack/methods/random_forest/random_forest_main.cpp ++++ b/src/mlpack/methods/random_forest/random_forest_main.cpp +@@ -20,12 +20,27 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Random forests", ++ // Short description. ++ "An implementation of the standard random forest algorithm by Leo Breiman " ++ "for classification. Given labeled data, a random forest can be trained " ++ "and saved for future use; or, a pre-trained random forest can be used for " ++ "classification.", ++ // Long description. + "This program is an implementation of the standard random forest " + "classification algorithm by Leo Breiman. A random forest can be " + "trained and saved for later use, or a random forest may be loaded " + "and predictions or class probabilities for points may be generated." + "\n\n" +- "This documentation will be rewritten once #880 is merged."); ++ "This documentation will be rewritten once #880 is merged.", ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@hoeffding_tree", "#hoeffding_tree"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("Random forest on Wikipedia", ++ "https://en.wikipedia.org/wiki/Random_forest"), ++ SEE_ALSO("Random forests (pdf)", ++ "https://link.springer.com/content/pdf/10.1023/A:1010933404324.pdf"), ++ SEE_ALSO("mlpack::tree::RandomForest C++ class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1RandomForest.html")); + + PARAM_MATRIX_IN("training", "Training dataset.", "t"); + PARAM_UROW_IN("labels", "Labels for training dataset.", "l"); +diff --git a/src/mlpack/methods/range_search/CMakeLists.txt b/src/mlpack/methods/range_search/CMakeLists.txt +index 9d29343db..68a020315 100644 +--- a/src/mlpack/methods/range_search/CMakeLists.txt ++++ b/src/mlpack/methods/range_search/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(range_search) + #add_python_binding(range_search) ++add_markdown_docs(range_search "cli" "geometry") +diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp +index 27e49caf1..c50bb709b 100644 +--- a/src/mlpack/methods/range_search/range_search_main.cpp ++++ b/src/mlpack/methods/range_search/range_search_main.cpp +@@ -29,6 +29,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("Range Search", ++ // Short description. ++ "An implementation of range search with single-tree and dual-tree " ++ "algorithms. Given a set of reference points and a set of query points and" ++ " a range, this can find the set of reference points within the desired " ++ "range for each query point, and any trees built during the computation can" ++ " be saved for reuse with future range searches.", ++ // Long description. + "This program implements range search with a Euclidean distance metric. " + "For a given query point, a given range, and a given set of reference " + "points, the program will return all of the reference points with distance " +@@ -55,7 +62,14 @@ PROGRAM_INFO("Range Search", + " resultant CSV-like files may not be loadable by many programs. However, " + "at this time a better way to store this non-square result is not known. " + "As a result, any output files will be written as CSVs in this manner, " +- "regardless of the given extension."); ++ "regardless of the given extension.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Range searching on Wikipedia", ++ "https://en.wikipedia.org/wiki/Range_searching"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::range::RangeSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1range_1_1RangeSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/rann/CMakeLists.txt b/src/mlpack/methods/rann/CMakeLists.txt +index 457a11dd6..3ba78994a 100644 +--- a/src/mlpack/methods/rann/CMakeLists.txt ++++ b/src/mlpack/methods/rann/CMakeLists.txt +@@ -37,3 +37,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # reference sets. + add_cli_executable(krann) + add_python_binding(krann) ++add_markdown_docs(krann "cli;python" "geometry") +diff --git a/src/mlpack/methods/rann/krann_main.cpp b/src/mlpack/methods/rann/krann_main.cpp +index 703fdd085..6a74e5c10 100644 +--- a/src/mlpack/methods/rann/krann_main.cpp ++++ b/src/mlpack/methods/rann/krann_main.cpp +@@ -30,6 +30,13 @@ typedef RAModel RANNModel; + + // Information about the program itself. + PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", ++ // Short description. ++ "An implementation of rank-approximate k-nearest-neighbor search (kRANN) " ++ " using single-tree and dual-tree algorithms. Given a set of reference " ++ "points and query points, this can find the k nearest neighbors in the " ++ "reference set of each query point using trees; trees that are built can " ++ "be saved for future use.", ++ // Long description. + "This program will calculate the k rank-approximate-nearest-neighbors of a " + "set of points. You may specify a separate set of reference points and " + "query points, or just a reference set which will be used as both the " +@@ -53,7 +60,15 @@ PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", + "neighbors output file corresponds to the index of the point in the " + "reference set which is the i'th nearest neighbor from the point in the " + "query set with index j. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("Rank-approximate nearest neighbor search: Retaining meaning and " ++ "speed in high dimensions (pdf)", "https://papers.nips.cc/paper/3864-" ++ "rank-approximate-nearest-neighbor-search-retaining-meaning-and-speed-" ++ "in-high-dimensions.pdf"), ++ SEE_ALSO("mlpack::neighbor::RASearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1RASearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/softmax_regression/CMakeLists.txt b/src/mlpack/methods/softmax_regression/CMakeLists.txt +index c6770f7c4..60a7d4cff 100644 +--- a/src/mlpack/methods/softmax_regression/CMakeLists.txt ++++ b/src/mlpack/methods/softmax_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(softmax_regression) + add_python_binding(softmax_regression) ++add_markdown_docs(softmax_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +index 97b54a2a5..4e3f88e76 100644 +--- a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp ++++ b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +@@ -24,11 +24,18 @@ using namespace mlpack::regression; + using namespace mlpack::util; + + // Define parameters for the executable. +-PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " +- "a generalization of logistic regression to the multiclass case, and has " +- "support for L2 regularization. The program is able to train a model, load" +- " an existing model, and give predictions (and optionally their accuracy) " +- "for test data." ++PROGRAM_INFO("Softmax Regression", ++ // Short description. ++ "An implementation of softmax regression for classification, which is a " ++ "multiclass generalization of logistic regression. Given labeled data, a " ++ "softmax regression model can be trained and saved for future use, or, a " ++ "pre-trained softmax regression model can be used for classification of " ++ "new points.", ++ // Long description. ++ "This program performs softmax regression, a generalization of logistic " ++ "regression to the multiclass case, and has support for L2 regularization. " ++ " The program is able to train a model, load an existing model, and give " ++ "predictions (and optionally their accuracy) for test data." + "\n\n" + "Training a softmax regression model is done by giving a file of training " + "points with the " + PRINT_PARAM_STRING("training") + " parameter and their" +@@ -71,7 +78,14 @@ PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " + " " + PRINT_DATASET("predictions") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("softmax_regression", "input_model", "sr_model", "test", +- "test_points", "predictions", "predictions")); ++ "test_points", "predictions", "predictions"), ++ SEE_ALSO("@logistic_regression", "#logistic_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Multinomial logistic regression (softmax regression) on " ++ "Wikipedia", ++ "https://en.wikipedia.org/wiki/Multinomial_logistic_regression"), ++ SEE_ALSO("mlpack::regression::SoftmaxRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1SoftmaxRegression.html")); + + // Required options. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/sparse_coding/CMakeLists.txt b/src/mlpack/methods/sparse_coding/CMakeLists.txt +index cfd1108cb..7bb0f7b79 100644 +--- a/src/mlpack/methods/sparse_coding/CMakeLists.txt ++++ b/src/mlpack/methods/sparse_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(sparse_coding) + add_python_binding(sparse_coding) ++add_markdown_docs(sparse_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +index e4b561f53..515cb78eb 100644 +--- a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp ++++ b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +@@ -22,12 +22,20 @@ using namespace mlpack::math; + using namespace mlpack::sparse_coding; + using namespace mlpack::util; + +-PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " +- "Dictionary Learning, which achieves sparsity via an l1-norm regularizer on" +- " the codes (LASSO) or an (l1+l2)-norm regularizer on the codes (the " +- "Elastic Net). Given a dense data matrix X with d dimensions and n points," +- " sparse coding seeks to find a dense dictionary matrix D with k atoms in " +- "d dimensions, and a sparse coding matrix Z with n points in k dimensions." ++PROGRAM_INFO("Sparse Coding", ++ // Short description. ++ "An implementation of Sparse Coding with Dictionary Learning. Given a " ++ "dataset, this will decompose the dataset into a sparse combination of a " ++ "few dictionary elements, where the dictionary is learned during " ++ "computation; a dictionary can be reused for future sparse coding of new " ++ "points.", ++ // Long description. ++ "An implementation of Sparse Coding with Dictionary Learning, which " ++ "achieves sparsity via an l1-norm regularizer on the codes (LASSO) or an " ++ "(l1+l2)-norm regularizer on the codes (the Elastic Net). Given a dense " ++ "data matrix X with d dimensions and n points, sparse coding seeks to find " ++ "a dense dictionary matrix D with k atoms in d dimensions, and a sparse " ++ "coding matrix Z with n points in k dimensions." + "\n\n" + "The original data matrix X can then be reconstructed as Z * D. Therefore," + " this program finds a representation of each point in X as a sparse linear" +@@ -62,7 +70,18 @@ PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " + PRINT_DATASET("codes") + ": " + "\n\n" + + PRINT_CALL("sparse_coding", "input_model", "model", "test", "otherdata", +- "codes", "codes")); ++ "codes", "codes"), ++ SEE_ALSO("@local_coordinate_coding", "#local_coordinate_coding"), ++ SEE_ALSO("Sparse dictionary learning on Wikipedia", ++ "https://en.wikipedia.org/wiki/Sparse_dictionary_learning"), ++ SEE_ALSO("Efficient sparse coding algorithms (pdf)", ++ "http://papers.nips.cc/paper/2979-efficient-sparse-coding-" ++ "algorithms.pdf"), ++ SEE_ALSO("Regularization and variable selection via the elastic net", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4696&" ++ "rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::sparse_coding::SparseCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1sparse__coding_1_1SparseCoding.html")); + + // Train the model. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +-- +2.20.1 + diff --git a/_src/markdown-patches/mlpack-3.0.2-markdown.patch b/_src/markdown-patches/mlpack-3.0.2-markdown.patch new file mode 100644 index 000000000..d439fb836 --- /dev/null +++ b/_src/markdown-patches/mlpack-3.0.2-markdown.patch @@ -0,0 +1,7189 @@ +From 6c44b0ec4e7a74d6bd9dfdf5c91591a2e76be269 Mon Sep 17 00:00:00 2001 +From: Ryan Curtin +Date: Wed, 6 Mar 2019 19:56:30 -0500 +Subject: [PATCH] Backport Markdown support onto mlpack 3.0.2. + +--- + CMake/RunProgram.cmake | 8 + + CMakeLists.txt | 14 + + src/mlpack/CMakeLists.txt | 9 +- + src/mlpack/bindings/CMakeLists.txt | 2 + + src/mlpack/bindings/cli/CMakeLists.txt | 2 + + .../bindings/cli/default_param_impl.hpp | 59 +- + src/mlpack/bindings/cli/end_program.hpp | 2 +- + .../bindings/cli/get_printable_type.hpp | 79 +++ + .../bindings/cli/get_printable_type_impl.hpp | 109 ++++ + .../bindings/cli/print_doc_functions.hpp | 38 ++ + .../bindings/cli/print_doc_functions_impl.hpp | 110 +++- + src/mlpack/bindings/cli/print_help.cpp | 6 +- + src/mlpack/bindings/cli/print_type_doc.hpp | 81 +++ + .../bindings/cli/print_type_doc_impl.hpp | 173 ++++++ + src/mlpack/bindings/cli/string_type_param.hpp | 6 - + .../bindings/cli/string_type_param_impl.hpp | 10 - + src/mlpack/bindings/markdown/CMakeLists.txt | 209 +++++++ + .../markdown/MarkdownCategories.cmake | 11 + + src/mlpack/bindings/markdown/binding_info.cpp | 49 ++ + src/mlpack/bindings/markdown/binding_info.hpp | 62 ++ + .../bindings/markdown/default_param.hpp | 45 ++ + .../markdown/generate_markdown.binding.cpp.in | 46 ++ + .../markdown/generate_markdown.binding.hpp.in | 28 + + .../markdown/generate_markdown.cpp.in | 114 ++++ + .../bindings/markdown/get_binding_name.cpp | 40 ++ + .../bindings/markdown/get_binding_name.hpp | 30 + + src/mlpack/bindings/markdown/get_param.hpp | 38 ++ + .../bindings/markdown/get_printable_param.hpp | 126 ++++ + .../markdown/get_printable_param_name.hpp | 83 +++ + .../get_printable_param_name_impl.hpp | 79 +++ + .../markdown/get_printable_param_value.hpp | 88 +++ + .../get_printable_param_value_impl.hpp | 84 +++ + .../bindings/markdown/get_printable_type.hpp | 62 ++ + .../bindings/markdown/is_serializable.hpp | 63 ++ + src/mlpack/bindings/markdown/md_option.hpp | 109 ++++ + .../bindings/markdown/print_doc_functions.hpp | 109 ++++ + .../markdown/print_doc_functions_impl.hpp | 540 ++++++++++++++++++ + src/mlpack/bindings/markdown/print_docs.cpp | 218 +++++++ + src/mlpack/bindings/markdown/print_docs.hpp | 33 ++ + .../bindings/markdown/print_type_doc.hpp | 46 ++ + .../bindings/markdown/program_doc_wrapper.hpp | 41 ++ + .../bindings/markdown/res/change_language.js | 102 ++++ + .../bindings/markdown/res/formatting.css | 142 +++++ + src/mlpack/bindings/markdown/res/menu_bg.png | Bin 0 -> 395 bytes + src/mlpack/bindings/python/CMakeLists.txt | 4 + + src/mlpack/bindings/python/default_param.hpp | 95 +++ + .../bindings/python/default_param_impl.hpp | 147 +++++ + .../bindings/python/get_cython_type.hpp | 10 - + .../bindings/python/get_printable_type.hpp | 120 ++++ + .../python/get_printable_type_impl.hpp | 151 +++++ + src/mlpack/bindings/python/print_doc.hpp | 22 +- + .../bindings/python/print_doc_functions.hpp | 26 + + .../python/print_doc_functions_impl.hpp | 124 +++- + .../python/print_input_processing.hpp | 1 - + src/mlpack/bindings/python/print_type_doc.hpp | 81 +++ + .../bindings/python/print_type_doc_impl.hpp | 162 ++++++ + src/mlpack/bindings/python/py_option.hpp | 4 + + .../python/tests/test_python_binding_main.cpp | 1 + + src/mlpack/core/util/cli.cpp | 3 +- + src/mlpack/core/util/mlpack_main.hpp | 68 ++- + src/mlpack/core/util/param.hpp | 31 +- + src/mlpack/core/util/program_doc.cpp | 30 +- + src/mlpack/core/util/program_doc.hpp | 17 +- + src/mlpack/methods/CMakeLists.txt | 4 + + src/mlpack/methods/adaboost/CMakeLists.txt | 1 + + src/mlpack/methods/adaboost/adaboost_main.cpp | 21 +- + src/mlpack/methods/approx_kfn/CMakeLists.txt | 1 + + .../methods/approx_kfn/approx_kfn_main.cpp | 19 +- + src/mlpack/methods/cf/CMakeLists.txt | 1 + + src/mlpack/methods/cf/cf_main.cpp | 25 +- + src/mlpack/methods/dbscan/CMakeLists.txt | 1 + + src/mlpack/methods/dbscan/dbscan_main.cpp | 12 +- + .../methods/decision_stump/CMakeLists.txt | 1 + + .../decision_stump/decision_stump_main.cpp | 12 +- + .../methods/decision_tree/CMakeLists.txt | 1 + + .../decision_tree/decision_tree_main.cpp | 16 +- + src/mlpack/methods/det/CMakeLists.txt | 1 + + src/mlpack/methods/det/det_main.cpp | 15 +- + src/mlpack/methods/emst/CMakeLists.txt | 1 + + src/mlpack/methods/emst/emst_main.cpp | 13 +- + src/mlpack/methods/fastmks/CMakeLists.txt | 1 + + src/mlpack/methods/fastmks/fastmks_main.cpp | 15 +- + src/mlpack/methods/gmm/CMakeLists.txt | 5 + + src/mlpack/methods/gmm/gmm_generate_main.cpp | 12 +- + .../methods/gmm/gmm_probability_main.cpp | 13 +- + src/mlpack/methods/gmm/gmm_train_main.cpp | 13 +- + src/mlpack/methods/hmm/CMakeLists.txt | 7 + + src/mlpack/methods/hmm/hmm_generate_main.cpp | 18 +- + src/mlpack/methods/hmm/hmm_loglik_main.cpp | 19 +- + src/mlpack/methods/hmm/hmm_train_main.cpp | 21 +- + src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 19 +- + .../methods/hoeffding_trees/CMakeLists.txt | 1 + + .../hoeffding_trees/hoeffding_tree_main.cpp | 14 +- + src/mlpack/methods/kernel_pca/CMakeLists.txt | 1 + + .../methods/kernel_pca/kernel_pca_main.cpp | 17 +- + src/mlpack/methods/kmeans/CMakeLists.txt | 1 + + src/mlpack/methods/kmeans/kmeans_main.cpp | 32 +- + src/mlpack/methods/lars/CMakeLists.txt | 1 + + src/mlpack/methods/lars/lars_main.cpp | 21 +- + .../methods/linear_regression/CMakeLists.txt | 1 + + .../linear_regression_main.cpp | 14 +- + .../local_coordinate_coding/CMakeLists.txt | 1 + + .../local_coordinate_coding_main.cpp | 14 +- + .../logistic_regression/CMakeLists.txt | 1 + + .../logistic_regression_main.cpp | 17 +- + src/mlpack/methods/lsh/CMakeLists.txt | 1 + + src/mlpack/methods/lsh/lsh_main.cpp | 17 +- + src/mlpack/methods/mean_shift/CMakeLists.txt | 1 + + .../methods/mean_shift/mean_shift_main.cpp | 23 +- + src/mlpack/methods/naive_bayes/CMakeLists.txt | 1 + + src/mlpack/methods/naive_bayes/nbc_main.cpp | 14 +- + src/mlpack/methods/nca/CMakeLists.txt | 1 + + src/mlpack/methods/nca/nca_main.cpp | 15 +- + .../methods/neighbor_search/CMakeLists.txt | 3 + + .../methods/neighbor_search/kfn_main.cpp | 14 +- + .../methods/neighbor_search/knn_main.cpp | 17 +- + src/mlpack/methods/nmf/CMakeLists.txt | 1 + + src/mlpack/methods/nmf/nmf_main.cpp | 25 +- + src/mlpack/methods/pca/CMakeLists.txt | 1 + + src/mlpack/methods/pca/pca_main.cpp | 24 +- + src/mlpack/methods/perceptron/CMakeLists.txt | 1 + + .../methods/perceptron/perceptron_main.cpp | 13 +- + src/mlpack/methods/preprocess/CMakeLists.txt | 7 + + .../preprocess/preprocess_binarize_main.cpp | 13 +- + .../preprocess/preprocess_describe_main.cpp | 22 +- + .../preprocess/preprocess_imputer_main.cpp | 15 +- + .../preprocess/preprocess_split_main.cpp | 20 +- + src/mlpack/methods/radical/CMakeLists.txt | 1 + + src/mlpack/methods/radical/radical_main.cpp | 26 +- + .../methods/random_forest/CMakeLists.txt | 1 + + .../random_forest/random_forest_main.cpp | 17 +- + .../methods/range_search/CMakeLists.txt | 1 + + .../range_search/range_search_main.cpp | 16 +- + src/mlpack/methods/rann/CMakeLists.txt | 1 + + src/mlpack/methods/rann/krann_main.cpp | 17 +- + .../methods/softmax_regression/CMakeLists.txt | 1 + + .../softmax_regression_main.cpp | 26 +- + .../methods/sparse_coding/CMakeLists.txt | 1 + + .../sparse_coding/sparse_coding_main.cpp | 33 +- + 139 files changed, 5011 insertions(+), 205 deletions(-) + create mode 100644 CMake/RunProgram.cmake + create mode 100644 src/mlpack/bindings/cli/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/cli/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/CMakeLists.txt + create mode 100644 src/mlpack/bindings/markdown/MarkdownCategories.cmake + create mode 100644 src/mlpack/bindings/markdown/binding_info.cpp + create mode 100644 src/mlpack/bindings/markdown/binding_info.hpp + create mode 100644 src/mlpack/bindings/markdown/default_param.hpp + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.cpp.in + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.cpp + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/markdown/is_serializable.hpp + create mode 100644 src/mlpack/bindings/markdown/md_option.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.cpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.hpp + create mode 100644 src/mlpack/bindings/markdown/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/markdown/program_doc_wrapper.hpp + create mode 100644 src/mlpack/bindings/markdown/res/change_language.js + create mode 100644 src/mlpack/bindings/markdown/res/formatting.css + create mode 100644 src/mlpack/bindings/markdown/res/menu_bg.png + create mode 100644 src/mlpack/bindings/python/default_param.hpp + create mode 100644 src/mlpack/bindings/python/default_param_impl.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc_impl.hpp + +diff --git a/CMake/RunProgram.cmake b/CMake/RunProgram.cmake +new file mode 100644 +index 000000000..6ae385314 +--- /dev/null ++++ b/CMake/RunProgram.cmake +@@ -0,0 +1,8 @@ ++# RunProgram.cmake: a CMake script that actually runs the given program to ++# generate a file, which is output into the given directory. ++# ++# This script depends on the following arguments: ++# ++# PROGRAM: the program to run to. ++# OUTPUT_FILE: the file to store the output in. ++execute_process(COMMAND ${PROGRAM} OUTPUT_FILE ${OUTPUT_FILE}) +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 165828b48..9d989e48e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -15,6 +15,20 @@ option(BUILD_CLI_EXECUTABLES "Build command-line executables." ON) + option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) + option(BUILD_SHARED_LIBS + "Compile shared libraries (if OFF, static libraries are compiled)." ON) ++ ++# Currently Python bindings aren't known to build successfully on Windows, so ++# set BUILD_PYTHON_BINDINGS to OFF when the platform is Windows. ++if (WIN32) ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." OFF) ++ message(WARNING "By default Python bindings are not compiled for Windows because they are not known to work. Set BUILD_PYTHON_BINDINGS to ON if you want them built.") ++else () ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) ++endif() ++ ++# Build Markdown bindings for documentation. This is used as part of website ++# generation. ++option(BUILD_MARKDOWN_BINDINGS "Build Markdown bindings for website documentation." OFF) ++ + option(BUILD_WITH_COVERAGE + "Build with support for code coverage tools (gcc only)." OFF) + option(MATHJAX +diff --git a/src/mlpack/CMakeLists.txt b/src/mlpack/CMakeLists.txt +index 29c545c9e..b555e0100 100644 +--- a/src/mlpack/CMakeLists.txt ++++ b/src/mlpack/CMakeLists.txt +@@ -14,7 +14,7 @@ set(DIRS + ) + + foreach(dir ${DIRS}) +- add_subdirectory(${dir}) ++ add_subdirectory(${dir}) + endforeach() + + # MLPACK_SRCS is set in the subdirectories. The dependencies (MLPACK_LIBRARIES) +@@ -40,8 +40,7 @@ if (NOT BUILD_SHARED_LIBS) + add_definitions(-DMLPACK_STATIC_DEFINE) + endif () + +-target_link_libraries(mlpack +- ${MLPACK_LIBRARIES}) ++target_link_libraries(mlpack ${MLPACK_LIBRARIES}) + + set_target_properties(mlpack + PROPERTIES +@@ -122,3 +121,7 @@ if (BUILD_PYTHON_BINDINGS) + configure_file(${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/setup.py.in + ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py) + endif () ++ ++# If we are building Markdown documentation, we have to run some setup after we ++# recurse into methods/. If not, this function is empty. ++post_markdown_setup() +diff --git a/src/mlpack/bindings/CMakeLists.txt b/src/mlpack/bindings/CMakeLists.txt +index f35249ddd..e726c515b 100644 +--- a/src/mlpack/bindings/CMakeLists.txt ++++ b/src/mlpack/bindings/CMakeLists.txt +@@ -1,6 +1,7 @@ + # All we have to do is recurse into the subdirectories. + set(DIRS + cli ++ markdown + python + tests + ) +@@ -9,6 +10,7 @@ foreach(dir ${DIRS}) + add_subdirectory(${dir}) + endforeach() + ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) + set(MLPACK_SRCS ${MLPACK_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) + set(DISABLE_CFLAGS ${DISABLE_CFLAGS} PARENT_SCOPE) +diff --git a/src/mlpack/bindings/cli/CMakeLists.txt b/src/mlpack/bindings/cli/CMakeLists.txt +index 83c50292e..280043fa6 100644 +--- a/src/mlpack/bindings/cli/CMakeLists.txt ++++ b/src/mlpack/bindings/cli/CMakeLists.txt +@@ -25,6 +25,8 @@ set(SOURCES + print_doc_functions_impl.hpp + print_help.hpp + print_help.cpp ++ print_type_doc.hpp ++ print_type_doc_impl.hpp + set_param.hpp + string_type_param.hpp + string_type_param_impl.hpp +diff --git a/src/mlpack/bindings/cli/default_param_impl.hpp b/src/mlpack/bindings/cli/default_param_impl.hpp +index 8c1740c1f..8c9ea7896 100644 +--- a/src/mlpack/bindings/cli/default_param_impl.hpp ++++ b/src/mlpack/bindings/cli/default_param_impl.hpp +@@ -32,7 +32,9 @@ std::string DefaultParamImpl( + std::tuple>>::type* /* junk */) + { + std::ostringstream oss; +- oss << boost::any_cast(data.value); ++ if (!std::is_same::value) ++ oss << boost::any_cast(data.value); ++ + return oss.str(); + } + +@@ -48,9 +50,35 @@ std::string DefaultParamImpl( + std::ostringstream oss; + const T& vector = boost::any_cast(data.value); + oss << "["; +- for (size_t i = 0; i < vector.size() - 1; ++i) +- oss << vector[i] << " "; +- oss << vector[vector.size() - 1] << "]"; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ + return oss.str(); + } + +@@ -67,39 +95,30 @@ std::string DefaultParamImpl( + } + + /** +- * Return the default value of a matrix option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a matrix option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::enable_if_c< + arma::is_arma_type::value || + std::is_same>::value>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ // The filename will always be empty. ++ return "''"; + } + + /** +- * Return the default value of a model option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a model option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::disable_if>::type* /* junk */, + const typename boost::enable_if>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ return "''"; + } + + +diff --git a/src/mlpack/bindings/cli/end_program.hpp b/src/mlpack/bindings/cli/end_program.hpp +index 031c49d5f..8e412db10 100644 +--- a/src/mlpack/bindings/cli/end_program.hpp ++++ b/src/mlpack/bindings/cli/end_program.hpp +@@ -50,7 +50,7 @@ inline void EndProgram() + while (it != parameters.end()) + { + // Now, figure out what type it is, and print it. +- // We can handle strings, ints, bools, floats, doubles. ++ // We can handle strings, ints, bools, doubles. + const util::ParamData& data = it->second; + std::string boostName; + CLI::GetSingleton().functionMap[data.tname]["MapParameterName"](data, +diff --git a/src/mlpack/bindings/cli/get_printable_type.hpp b/src/mlpack/bindings/cli/get_printable_type.hpp +new file mode 100644 +index 000000000..c4634c4ad +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/get_printable_type_impl.hpp b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..bb1ff85a5 +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ if (std::is_same::value) ++ return "flag"; ++ else if (std::is_same::value) ++ return "int"; ++ else if (std::is_same::value) ++ return "double"; ++ else if (std::is_same::value) ++ return "string"; ++ else ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ return "int vector"; ++ else if (std::is_same>::value) ++ return "string vector"; ++ else ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ return "2-d matrix file"; ++ else if (std::is_same>::value) ++ return "2-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else ++ throw std::invalid_argument("unknown Armadillo type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "2-d categorical matrix file"; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return data.cppType + " file"; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_doc_functions.hpp b/src/mlpack/bindings/cli/print_doc_functions.hpp +index cfc024b2c..81ce73816 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions.hpp +@@ -20,12 +20,38 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ ++/** ++ * Print documentation for each of the types. ++ */ ++inline std::string PrintTypeDocs(); ++ + /** + * Given a parameter type, print the corresponding value. + */ + template + inline std::string PrintValue(const T& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -36,6 +62,12 @@ inline std::string PrintDataset(const std::string& dataset); + */ + inline std::string PrintModel(const std::string& model); + ++/** ++ * Print the type of a parameter that a user would specify from the ++ * command-line. ++ */ ++inline std::string PrintType(const util::ParamData& param); ++ + /** + * Base case for recursion. + */ +@@ -56,6 +88,12 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +index 0f37e7d5e..96349f5dd 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +@@ -20,6 +20,31 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ return "mlpack_" + bindingName; ++} ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& /* bindingName */) ++{ ++ return ""; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return ""; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -35,6 +60,23 @@ inline std::string PrintValue(const T& value, bool quotes) + return oss.str(); + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -107,10 +149,76 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args) + { +- return util::HyphenateString("$ " + programName + " " + ++ return util::HyphenateString("$ " + GetBindingName(programName) + " " + + ProcessOptions(args...), 2); + } + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << "$ " << GetBindingName(programName); ++ ++ // Handle all options---first input options, then output options. ++ const std::map& parameters = CLI::Parameters(); ++ ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " "; ++ if (!it->second.required) ++ oss << "["; ++ ++ oss << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ ++ if (!it->second.required) ++ oss << "]"; ++ } ++ ++ // Now get the output options. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " [" << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ oss << "]"; ++ } ++ ++ return util::HyphenateString(oss.str(), 8); ++} ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_help.cpp b/src/mlpack/bindings/cli/print_help.cpp +index c6ef6c90d..ff62a7154 100644 +--- a/src/mlpack/bindings/cli/print_help.cpp ++++ b/src/mlpack/bindings/cli/print_help.cpp +@@ -118,7 +118,11 @@ void PrintHelp(const std::string& param) + } + + // Append default value to description. +- if (pass >= 1 && data.cppType != "bool") ++ if (pass >= 1 && (data.cppType == "int" || data.cppType == "double" || ++ data.cppType == "std::string" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector")) + { + std::string defaultValue; + CLI::GetSingleton().functionMap[data.tname]["DefaultParam"](data, +diff --git a/src/mlpack/bindings/cli/print_type_doc.hpp b/src/mlpack/bindings/cli/print_type_doc.hpp +new file mode 100644 +index 000000000..677854e60 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_type_doc_impl.hpp b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..3a5a20912 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +@@ -0,0 +1,173 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option. If not specified, it is false; if " ++ "specified, it is true."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A vector of integers, separated by commas (i.e., \"1,2,3\")."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A vector of strings, separated by commas (i.e., " ++ "\"hello\",\"goodbye\")."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ return "A data matrix filename. The file can be CSV (.csv), TSV (.csv), " ++ "ASCII (space-separated values, .txt), Armadillo ASCII (.txt), PGM " ++ "(.pgm), PPM (.ppm), Armadillo binary (.bin), or HDF5 (.h5, .hdf, " ++ ".hdf5, or .he5), if mlpack was compiled with HDF5 support. The type " ++ "of the data is detected by the extension of the filename. The storage" ++ " should be such that one row corresponds to one point, and one column " ++ "corresponds to one dimension (this is the typical storage format for " ++ "on-disk data). All values of the matrix will be loaded as double-" ++ "precision floating point data."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A data matrix filename, where the matrix holds only non-negative " ++ "integer values. This type is often used for labels or indices. The " ++ "file can be CSV (.csv), TSV (.csv), ASCII (space-separated values, " ++ ".txt), Armadillo ASCII (.txt), PGM (.pgm), PPM (.ppm), Armadillo " ++ "binary (.bin), or HDF5 (.h5, .hdf, .hdf5, or .he5), if mlpack was " ++ "compiled with HDF5 support. The type of the data is detected by the " ++ "extension of the filename. The storage should be such that one row " ++ "corresponds to one point, and one column corresponds to one dimension " ++ "(this is the typical storage format for on-disk data). All values of " ++ "the matrix will be loaded as unsigned integers."; ++ } ++ else if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "A one-dimensional vector filename. This file can take the same " ++ "formats as the data matrix filenames; however, it must either contain " ++ "one row and many columns, or one column and many rows."; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "A one-dimensional vector filename, where the matrix holds only non-" ++ "negative integer values. This type is typically used for labels or " ++ "predictions or other indices. This file can take the same formats as " ++ "the data matrix filenames; however, it must either contain one row and" ++ " many columns, or one column and many rows."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A filename for a data matrix that can contain categorical " ++ "(non-numeric) data. If the file contains only numeric data, then the " ++ "same formats for regular data matrices can be used. If the file " ++ "contains strings or other values that can't be parsed as numbers, then " ++ "the type to be loaded must be CSV (.csv) or ARFF (.arff). Any non-" ++ "numeric data will be converted to an unsigned integer value, and " ++ "dimensions where the data is converted will be treated as categorical " ++ "dimensions. When using this format, there is no need for one-hot " ++ "encoding of categorical data."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "A filename containing an mlpack model. These can have one of three " ++ "formats: binary (.bin), text (.txt), and XML (.xml). The XML format " ++ "produces the largest (but most human-readable) files, while the binary " ++ "format can be significantly more compact and quicker to load and save."; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/string_type_param.hpp b/src/mlpack/bindings/cli/string_type_param.hpp +index 28caa8991..702fb4668 100644 +--- a/src/mlpack/bindings/cli/string_type_param.hpp ++++ b/src/mlpack/bindings/cli/string_type_param.hpp +@@ -74,12 +74,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + const void* /* input */, + void* output); + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output); +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/cli/string_type_param_impl.hpp b/src/mlpack/bindings/cli/string_type_param_impl.hpp +index 01065086e..86fb96302 100644 +--- a/src/mlpack/bindings/cli/string_type_param_impl.hpp ++++ b/src/mlpack/bindings/cli/string_type_param_impl.hpp +@@ -80,16 +80,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + *outstr = "string"; + } + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output) +-{ +- std::string* outstr = (std::string*) output; +- *outstr = "float"; +-} +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/markdown/CMakeLists.txt b/src/mlpack/bindings/markdown/CMakeLists.txt +new file mode 100644 +index 000000000..b6b31cfba +--- /dev/null ++++ b/src/mlpack/bindings/markdown/CMakeLists.txt +@@ -0,0 +1,209 @@ ++macro (not_found_return message) ++ message(STATUS "${message}") ++ ++ macro (add_markdown_docs name languages category) ++ # Do nothing. ++ endmacro() ++ ++ function (post_markdown_setup) ++ # Do nothing. ++ endfunction () ++ ++ return () ++endmacro () ++ ++if (NOT BUILD_MARKDOWN_BINDINGS) ++ not_found_return("Not building Markdown bindings.") ++endif () ++ ++# We don't need to find any libraries or anything to generate markdown ++# documentation. ++ ++# Get categories. The list of allowable categories for add_markdown_docs() is ++# in that file. ++include(MarkdownCategories.cmake) ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) ++ ++# Add sources for Markdown bindings. ++set(SOURCES ++ "binding_info.hpp" ++ "binding_info.cpp" ++ "default_param.hpp" ++ "get_binding_name.hpp" ++ "get_binding_name.cpp" ++ "get_param.hpp" ++ "get_printable_param.hpp" ++ "get_printable_param_name.hpp" ++ "get_printable_param_name_impl.hpp" ++ "get_printable_type.hpp" ++ "md_option.hpp" ++ "print_doc_functions.hpp" ++ "print_doc_functions_impl.hpp" ++ "print_docs.hpp" ++ "print_docs.cpp" ++ "print_type_doc.hpp" ++ "program_doc_wrapper.hpp" ++) ++ ++# Copy all Markdown sources to the build directory. ++add_custom_target(markdown_copy) ++add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++foreach(file ${SOURCES}) ++ add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} ARGS -E copy ++ ${CMAKE_CURRENT_SOURCE_DIR}/${file} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++endforeach() ++ ++# Create the add_markdown_docs() macro. It's meant to be used as ++# 'add_markdown_docs(knn, "cli;python;julia", "classification")', for instance. ++# See the file 'MarkdownCategories.cmake' for valid categories that can be used ++# by the macro. ++macro (add_markdown_docs name languages category) ++ ++ # First, make sure that the category is a valid category. ++ list(FIND MARKDOWN_CATEGORIES ${category} cat_index) ++ if (${cat_index} EQUAL -1) ++ string(CONCAT error_str "add_markdown_docs(): unknown category ${category}!" ++ " See the categories in " ++ "src/mlpack/bindings/markdown/MarkdownCategories.cmake.") ++ message(FATAL_ERROR "${ERROR_STR}") ++ endif () ++ ++ # Next, we should use configure_file() to generate each ++ # generate_markdown..cpp. We need to loop over all the languages for ++ # this binding to do that. ++ set(BINDING ${name}) ++ set(LANGUAGES_PUSH_BACK_CODE "") ++ set(PROGRAM_MAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${name}_main.cpp") ++ foreach (lang ${languages}) ++ set(LANGUAGES_PUSH_BACK_CODE ++ "${LANGUAGES_PUSH_BACK_CODE}\n languages.push_back(\"${lang}\");") ++ set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} ${lang}) ++ endforeach () ++ list(REMOVE_DUPLICATES MARKDOWN_ALL_LANGUAGES_LIST) ++ ++ # Do the actual file configuration. ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.hpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.hpp) ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.cpp) ++ ++ # Lastly, that generate_markdown..cpp should be added to the list of ++ # files to be compiled for the 'generate_markdown' target. We also need to ++ # add information about this binding to a set of variables we have to track. ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.hpp ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.cpp) ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} ${name}) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} ${category}) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++ set (MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) ++endmacro () ++ ++# After all the methods/ directories have been traversed, we can add the ++# 'generate_markdown' target. This function is run at the bottom of ++# methods/CMakeLists.txt. ++function (post_markdown_setup) ++ # We need to generate the program file. This consists of generating three ++ # things: ++ # ++ # - MARKDOWN_INCLUDES: the list of files to be included. ++ # - MARKDOWN_HEADER_CODE: the code used to print the header/sidebar. ++ # - MARKDOWN_CALL_CODE: the code to actually call the functions to print ++ # documentation for each binding. ++ ++ # Iterate over categories of binding. ++ list(LENGTH MARKDOWN_NAMES NUM_MARKDOWN_BINDINGS) ++ list(LENGTH MARKDOWN_CATEGORIES NUM_MARKDOWN_CATEGORIES) ++ math(EXPR cat_limit "${NUM_MARKDOWN_CATEGORIES} - 1") ++ foreach (i RANGE ${cat_limit}) ++ list (GET MARKDOWN_CATEGORIES ${i} cat) ++ # Put the things in this category in a div. ++ string(CONCAT header_code "${MARKDOWN_HEADER_CODE} cout << " ++ "\"
    \" << endl;\n " ++ "cout << \"
    ${cat}:
    \" << endl;\n") ++ set(MARKDOWN_HEADER_CODE ${header_code}) ++ ++ # Add an option for this binding. ++ math(EXPR range_limit "${NUM_MARKDOWN_BINDINGS} - 1") ++ foreach (j RANGE ${range_limit}) ++ list (GET MARKDOWN_NAME_CATEGORIES ${j} category) ++ if (NOT category STREQUAL cat) ++ continue () ++ endif () ++ ++ list (GET MARKDOWN_NAMES ${j} name) ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n Print${name}Headers();") ++ endforeach () ++ ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n cout << \"
    \" << endl << endl;") ++ endforeach () ++ ++ foreach (name ${MARKDOWN_NAMES}) ++ set (MARKDOWN_INCLUDE_CODE ++ "${MARKDOWN_INCLUDE_CODE}\n#include \"generate_markdown.${name}.hpp\"") ++ set (MARKDOWN_CALL_CODE "${MARKDOWN_CALL_CODE}\n Print${name}Docs();") ++ endforeach () ++ ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.cpp) ++ ++ # Remember that this is being run from some other directory, so we have to be ++ # explicit with the locations of the files we are compiling against. ++ add_executable(generate_markdown ++ "${BINDING_BINARY_DIR}/generate_markdown.cpp" ++ ${MARKDOWN_SRCS} ++ "${BINDING_SOURCE_DIR}/binding_info.hpp" ++ "${BINDING_SOURCE_DIR}/binding_info.cpp" ++ "${BINDING_SOURCE_DIR}/default_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.cpp" ++ "${BINDING_SOURCE_DIR}/get_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name_impl.hpp" ++ "${BINDING_SOURCE_DIR}/md_option.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions_impl.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.cpp" ++ "${BINDING_SOURCE_DIR}/program_doc_wrapper.hpp" ++ "${BINDING_SOURCE_DIR}/generate_markdown.cpp") ++ target_link_libraries(generate_markdown mlpack ${MLPACK_LIBRARIES}) ++ add_dependencies(generate_markdown markdown_copy) ++ set_target_properties(generate_markdown PROPERTIES ++ COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_MARKDOWN ++ RUNTIME_OUTPUT_DIRECTORY ${BINDING_BINARY_DIR}) ++ ++ add_custom_target(markdown ALL ++ ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/doc/ ++ COMMAND ${CMAKE_COMMAND} ++ -DPROGRAM=${BINDING_BINARY_DIR}/generate_markdown ++ -DOUTPUT_FILE=${CMAKE_BINARY_DIR}/doc/mlpack.md ++ -P ${CMAKE_SOURCE_DIR}/CMake/RunProgram.cmake ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/change_language.js ++ ${CMAKE_BINARY_DIR}/doc/res/change_languages.js ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/menu_bg.png ++ ${CMAKE_BINARY_DIR}/doc/res/menu_bg.png ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/formatting.css ++ ${CMAKE_BINARY_DIR}/doc/res/formatting.css ++ DEPENDS generate_markdown ++ COMMENT "Generating Markdown documentation for mlpack bindings...") ++endfunction () +diff --git a/src/mlpack/bindings/markdown/MarkdownCategories.cmake b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +new file mode 100644 +index 000000000..6b8d697cf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +@@ -0,0 +1,11 @@ ++# This is a list of categories of binding that Markdown can handle. If the ++# category you choose for a Markdown binding is not in this list, an error will ++# be thrown. ++set (MARKDOWN_CATEGORIES ++ "classification" ++ "regression" ++ "clustering" ++ "geometry" ++ "preprocessing" ++ "misc. / other" ++ "transformations") +diff --git a/src/mlpack/bindings/markdown/binding_info.cpp b/src/mlpack/bindings/markdown/binding_info.cpp +new file mode 100644 +index 000000000..0a61103d3 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.cpp +@@ -0,0 +1,49 @@ ++/** ++ * @file binding_info.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of BindingInfo functions. ++ */ ++#include "binding_info.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++util::ProgramDoc& BindingInfo::GetProgramDoc(const std::string& bindingName) ++{ ++ if (GetSingleton().map.count(bindingName) == 0) ++ { ++ throw std::invalid_argument("Binding name '" + bindingName + ++ "' not known!"); ++ } ++ ++ return GetSingleton().map.at(bindingName); ++} ++ ++/** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++void BindingInfo::RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc) ++{ ++ GetSingleton().map[bindingName] = programDoc; ++} ++ ++//! Get or modify the current language (don't set it to something invalid!). ++std::string& BindingInfo::Language() ++{ ++ return GetSingleton().language; ++} ++ ++BindingInfo& BindingInfo::GetSingleton() ++{ ++ static BindingInfo instance; ++ return instance; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/binding_info.hpp b/src/mlpack/bindings/markdown/binding_info.hpp +new file mode 100644 +index 000000000..6a99d2ebc +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file binding_name.hpp ++ * @author Ryan Curtin ++ * ++ * This file defines the BindingInfo singleton class that is used specifically ++ * for the Markdown bindings to map from a binding name (i.e. "knn") to ++ * multiple ProgramDoc objects, which are then used to generate the ++ * documentation. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The BindingInfo class is used by the Markdown documentation generator to ++ * store multiple ProgramDoc objects, indexed by both the binding name (i.e. ++ * "knn") and the language (i.e. "cli"). ++ */ ++class BindingInfo ++{ ++ public: ++ /** ++ * Return a ProgramDoc object for a given bindingName. ++ */ ++ static util::ProgramDoc& GetProgramDoc(const std::string& bindingName); ++ ++ /** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++ static void RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc); ++ ++ //! Get or modify the current language (don't set it to something invalid!). ++ static std::string& Language(); ++ ++ private: ++ //! Private constructor, so that only one instance can be created. ++ BindingInfo() { } ++ ++ //! Get the singleton. ++ static BindingInfo& GetSingleton(); ++ ++ //! Internally-held map for mapping a binding name to a ProgramDoc name. ++ std::unordered_map map; ++ ++ //! Holds the name of the language that we are currently printing. This is ++ //! modified before printing the documentation, and then used by ++ //! print_doc_functions.hpp during printing to print the correct language. ++ std::string language; ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/default_param.hpp b/src/mlpack/bindings/markdown/default_param.hpp +new file mode 100644 +index 000000000..78af807e0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/default_param.hpp +@@ -0,0 +1,45 @@ ++/** ++ * @file default_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get the default value of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the default value of a parameter into the output string. The type ++ * printed depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::DefaultParamImpl::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::DefaultParamImpl::type>(data); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +new file mode 100644 +index 000000000..18a07e5ec +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +@@ -0,0 +1,46 @@ ++/** ++ * @file generate_markdown.binding.cpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#define BINDING_NAME "${BINDING}" ++ ++#include ++#include "generate_markdown.${BINDING}.hpp" ++#include "binding_info.hpp" ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++static const std::string testName = "${BINDING}"; ++#include <${PROGRAM_MAIN_FILE}> ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintHeaders("${BINDING}", languages); ++} ++ ++void Print${BINDING}Docs() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintDocs("${BINDING}", languages); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +new file mode 100644 +index 000000000..3e81c9c3a +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +@@ -0,0 +1,28 @@ ++/** ++ * @file generate_markdown.binding.hpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++ ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers(); ++void Print${BINDING}Docs(); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +new file mode 100644 +index 000000000..abacae377 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +@@ -0,0 +1,114 @@ ++/** ++ * @file generate_markdown.cpp.in ++ * @author Ryan Curtin ++ * ++ * This file is configured by CMake to generate all of the Markdown required by ++ * the project. ++ */ ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++${MARKDOWN_INCLUDE_CODE} ++ ++using namespace mlpack; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++using namespace std; ++ ++int main() ++{ ++ // These come to use from CMake separated by semicolons. ++ string languageList = "${MARKDOWN_ALL_LANGUAGES_LIST}"; ++ vector languages; ++ size_t index; ++ while ((index = languageList.find(';')) != string::npos) ++ { ++ languages.push_back(languageList.substr(0, index)); ++ languageList = languageList.substr(index + 1); ++ } ++ languages.push_back(languageList); ++ ++ cout << "
    " << endl; ++ ++ // We need to create the input form selector for the language. ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << " " << endl; ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // The links to the data type sections get put here. ++ cout << " - [mlpack overview](#mlpack-overview){: .language-link #always }" ++ << endl; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << " - [data formats](#" << languages[i] << "_data-formats){: " ++ << ".language-link #" << languages[i] << " }" << endl; ++ } ++ ++ ${MARKDOWN_HEADER_CODE} ++ ++ cout << endl << "
    " << endl << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // Create all the headers for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "
    " << endl; ++ cout << "# " << util::GetVersion() << " " << PrintLanguage(languages[i]) ++ << " binding documentation" << endl; ++ cout << "
    " << endl; ++ } ++ ++ /** ++ * "mlpack overview" section. This will go at the top of the page. ++ */ ++ cout << "## mlpack overview" << endl; ++ cout << endl; ++ cout << "mlpack is an intuitive, fast, and flexible C++ machine learning " ++ "library with bindings to other languages. It is meant to be a machine " ++ "learning analog to LAPACK, and aims to implement a wide array of machine" ++ " learning methods and functions as a \"swiss army knife\" for machine " ++ "learning researchers."; ++ cout << endl << endl; ++ cout << "This reference page details mlpack's bindings to other languages. " ++ "Further useful mlpack documentation links are given below."; ++ cout << endl << endl; ++ cout << " - [mlpack homepage](https://www.mlpack.org/)" << endl; ++ cout << " - [mlpack on Github](https://github.com/mlpack/mlpack)" << endl; ++ cout << " - [mlpack main documentation page]" ++ << "(https://www.mlpack.org/docs.html)" << endl; ++ cout << endl; ++ ++ /** ++ * Discussion of different data types section. This goes just below the ++ * overview section at the top of the page. ++ */ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ cout << PrintTypeDocs() << endl; ++ } ++ ++ ${MARKDOWN_CALL_CODE} ++ cout << "
    " << endl << endl; ++ ++ // Make sure script gets included for changeLanguage(). ++ cout << "" << endl; ++} +diff --git a/src/mlpack/bindings/markdown/get_binding_name.cpp b/src/mlpack/bindings/markdown/get_binding_name.cpp +new file mode 100644 +index 000000000..67d229b6c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.cpp +@@ -0,0 +1,40 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#include "get_binding_name.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++std::string GetBindingName(const std::string& language, ++ const std::string& name) ++{ ++ // Unfortunately, every time a new binding is added, this code will need to be ++ // modified. ++ if (language == "cli") ++ { ++ // For command-line programs, all bindings have 'mlpack_' prepended to the ++ // name. ++ return "mlpack_" + name; ++ } ++ else if (language == "python") ++ { ++ // For Python bindings, the name is unchanged. ++ return name; ++ } ++ else ++ { ++ throw std::invalid_argument("Don't know how to compute binding name for " ++ "language \"" + language + "\"! Is the language specified in " ++ "src/mlpack/bindings/markdown/get_binding_name.cpp?"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/get_binding_name.hpp b/src/mlpack/bindings/markdown/get_binding_name.hpp +new file mode 100644 +index 000000000..21c55f05c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.hpp +@@ -0,0 +1,30 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given a language name and a binding name, return the name of that binding for ++ * that language. Note that if a new language is added to the mlpack bindings, ++ * this method will need to be updated so that documentation can be successfully ++ * generated for that language. ++ */ ++std::string GetBindingName(const std::string& language, ++ const std::string& name); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_param.hpp b/src/mlpack/bindings/markdown/get_param.hpp +new file mode 100644 +index 000000000..6c1611f49 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_param.hpp +@@ -0,0 +1,38 @@ ++/** ++ * @file get_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a parameter for a Markdown binding. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * All Markdown binding types are exactly what is held in the ParamData, so no ++ * special handling is necessary. ++ */ ++template ++void GetParam(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ util::ParamData& dmod = const_cast(d); ++ *((T**) output) = boost::any_cast(&dmod.value); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param.hpp b/src/mlpack/bindings/markdown/get_printable_param.hpp +new file mode 100644 +index 000000000..53a862b61 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param.hpp +@@ -0,0 +1,126 @@ ++/** ++ * @file get_printable_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a printable version of parameters. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print an option of a simple type. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a vector option, with spaces between it. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ const T& t = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ for (size_t i = 0; i < t.size(); ++i) ++ oss << t[i] << " "; ++ return oss.str(); ++} ++ ++/** ++ * Print a matrix option (this prints its size). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ // Get the matrix. ++ const T& matrix = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix"; ++ return oss.str(); ++} ++ ++/** ++ * Print a serializable class option (this prints the class name). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << data.cppType << " model at " << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a combination DatasetInfo/matrix parameter. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0) ++{ ++ // Get the matrix. ++ const T& tuple = boost::any_cast(data.value); ++ const arma::mat& matrix = std::get<1>(tuple); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix with dimension type " ++ << "information"; ++ return oss.str(); ++} ++ ++/** ++ * Print an option into a std::string. This should print a short, one-line ++ * representation of the object. The string will be stored in the output ++ * pointer. ++ * ++ * @param data Parameter data struct. ++ * @param input Unused parameter. ++ * @param output Output storage for the string. ++ */ ++template ++void GetPrintableParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParam::type>(data); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name.hpp b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +new file mode 100644 +index 000000000..9ceca6b4c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +@@ -0,0 +1,83 @@ ++/** ++ * @file get_printable_param_name.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamName( ++ const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamName::type>(d); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_name_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +new file mode 100644 +index 000000000..41b9c2972 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_param_name_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "--" + data.name; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value.hpp b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +new file mode 100644 +index 000000000..d58bc93bf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +@@ -0,0 +1,88 @@ ++/** ++ * @file get_printable_param_value.hpp ++ * @author Ryan Curtin ++ * ++ * Given a parameter value, print what the user might actually specify on the ++ * command line. Basically this adds ".csv" to types where data must be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamValue( ++ const util::ParamData& d, ++ const void* input, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamValue::type>(d, ++ *((std::string*) input)); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_value_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +new file mode 100644 +index 000000000..9fce0559b +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +@@ -0,0 +1,84 @@ ++/** ++ * @file get_printable_param_value_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter value that the user would specify on the command line ++ * depending on the type of the option. Basically this adds ".csv" to types ++ * that need to be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return input; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".csv"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".bin"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>>::type*) ++{ ++ return input + ".arff"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_type.hpp b/src/mlpack/bindings/markdown/get_printable_type.hpp +new file mode 100644 +index 000000000..2b292b937 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_type.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::GetPrintableType::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::GetPrintableType::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("GetPrintableType(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the type of a parameter. The type printed depends on the current ++ * setting of BindingInfo::Language(). ++ */ ++template ++std::string GetPrintableType(const util::ParamData& data) ++{ ++ std::string output; ++ GetPrintableType(data, (void*) NULL, (void*) &output); ++ return output; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/is_serializable.hpp b/src/mlpack/bindings/markdown/is_serializable.hpp +new file mode 100644 +index 000000000..b129487b9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/is_serializable.hpp +@@ -0,0 +1,63 @@ ++/** ++ * @file is_serializable.hpp ++ * @author Ryan Curtin ++ * ++ * Return a bool noting whether or not a parameter is serializable. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Return false, because the type is not serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::disable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return false, because even though the type is serializable, it is an ++ * Armadillo type not an mlpack model. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return true, because the type is serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0) ++{ ++ return true; ++} ++ ++/** ++ * Return whether or not the type is serializable. ++ */ ++template ++void IsSerializable(const util::ParamData& /* data */, ++ const void* /* input */, ++ void* output) ++{ ++ *((bool*) output) = IsSerializable::type>(); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/md_option.hpp b/src/mlpack/bindings/markdown/md_option.hpp +new file mode 100644 +index 000000000..dc728c4e9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/md_option.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file md_option.hpp ++ * @author Ryan Curtin ++ * ++ * The Markdown option type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++#define MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++ ++#include ++#include ++#include "default_param.hpp" ++#include "get_param.hpp" ++#include "get_printable_param.hpp" ++#include "get_printable_param_name.hpp" // For cli bindings. ++#include "get_printable_param_value.hpp" // For cli bindings. ++#include "get_printable_type.hpp" ++#include "is_serializable.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The Markdown option class. ++ */ ++template ++class MDOption ++{ ++ public: ++ /** ++ * Construct an MDOption object. When constructed, it will register itself ++ * with CLI. The testName parameter is not used and added for compatibility ++ * reasons. ++ */ ++ MDOption(const T defaultValue, ++ const std::string& identifier, ++ const std::string& description, ++ const std::string& alias, ++ const std::string& cppName, ++ const bool required = false, ++ const bool input = true, ++ const bool noTranspose = false, ++ const std::string& bindingName = "") ++ { ++ // Create the ParamData object to give to CLI. ++ util::ParamData data; ++ ++ data.desc = description; ++ data.name = identifier; ++ data.tname = TYPENAME(T); ++ data.alias = alias[0]; ++ data.wasPassed = false; ++ data.noTranspose = noTranspose; ++ data.required = required; ++ data.input = input; ++ data.loaded = false; ++ // Several options from Python and CLI bindings are persistent. ++ if (identifier == "verbose" || identifier == "copy_all_inputs" || ++ identifier == "help" || identifier == "info" || identifier == "version") ++ data.persistent = true; ++ else ++ data.persistent = false; ++ data.cppType = cppName; ++ ++ // Every parameter we'll get from Markdown will have the correct type. ++ data.value = boost::any(defaultValue); ++ ++ // Restore the parameters for this program. ++ if (identifier != "verbose" && identifier != "copy_all_inputs") ++ CLI::RestoreSettings(bindingName, false); ++ ++ // Set the function pointers that we'll need. Most of these simply delegate ++ // to the current binding type's implementation. Any new language will need ++ // to have all of these implemented, and the Markdown implementation will ++ // need to properly delegate. ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetParam"] = &GetParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = ++ &GetPrintableParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamName"] = ++ &GetPrintableParamName; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamValue"] = ++ &GetPrintableParamValue; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableType"] = ++ &GetPrintableType; ++ CLI::GetSingleton().functionMap[data.tname]["IsSerializable"] = ++ &IsSerializable; ++ ++ // Add the option. ++ CLI::Add(std::move(data)); ++ if (identifier != "verbose" && identifier != "copy_all_inputs" && ++ identifier != "help" && identifier != "info" && identifier != "version") ++ CLI::StoreSettings(bindingName); ++ CLI::ClearSettings(); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions.hpp b/src/mlpack/bindings/markdown/print_doc_functions.hpp +new file mode 100644 +index 000000000..2430fa512 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file print_doc_functions.hpp ++ * @author Ryan Curtin ++ * ++ * This file wraps the different printing functionality of different binding ++ * types. If a new binding type is added, this code will need to be modified so ++ * that Markdown can be printed. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language); ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(const std::string& language); ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs(); ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes); ++ ++/** ++ * Print the default value of an option, unless it is required (in which case ++ * Markdown italicized '--' is printed). ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset); ++ ++/** ++ * Print a model type parameter (add .bin and return). ++ */ ++inline std::string PrintModel(const std::string& model); ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args); ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName); ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d); ++ ++/** ++ * Return whether or not a runtime check on parameters should be ignored. ++ */ ++template ++inline bool IgnoreCheck(const T& t); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "print_doc_functions_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +new file mode 100644 +index 000000000..13359441d +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +@@ -0,0 +1,540 @@ ++/** ++ * @file print_doc_functions_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Call out to different printing functionality for different binding languages. ++ * If a new binding is added, this code must be modified. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++ ++#include "print_doc_functions.hpp" ++#include "binding_info.hpp" ++#include "print_type_doc.hpp" ++#include "get_printable_type.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::GetBindingName(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::GetBindingName(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language) ++{ ++ if (language == "cli") ++ { ++ return "CLI"; ++ } ++ else if (language == "python") ++ { ++ return "Python"; ++ } ++ else ++ { ++ throw std::invalid_argument("PrintLanguage(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintImport(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintImport(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintImport(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintOutputOptionInfo(); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintOutputOptionInfo(); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintOutputOptionInfo(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++namespace priv { ++ ++// We'll need a fake class for printing model type documentation. ++class mlpackModel ++{ ++ public: ++ // Fake serialization to make SFINAE work right for this type. ++ template ++ void serialize(Archive&, const unsigned int) {} ++}; ++ ++} // namespace priv ++ ++// Utility function that returns the first word (as delimited by spaces) of a ++// string. ++inline std::string ToUnderscores(const std::string& str) ++{ ++ std::string ret(str); ++ std::replace(ret.begin(), ret.end(), ' ', '_'); ++ return ret; ++} ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs() ++{ ++ std::ostringstream oss; ++ oss << "
    " << std::endl; ++ oss << "## data formats" << std::endl; ++ oss << "{: .language-types-h2 #" << BindingInfo::Language() ++ << "_data-formats }" << std::endl; ++ oss << std::endl; ++ ++ // Iterate through each of the types that we care about. ++ oss << "mlpack bindings for " << PrintLanguage(BindingInfo::Language()) ++ << " take and return a restricted set of types, for simplicity. These " ++ << "include primitive types, matrix/vector types, categorical matrix " ++ << "types, and model types. Each type is detailed below." << std::endl; ++ oss << std::endl; ++ ++ // Create fake ParamData to pass around. ++ util::ParamData data; ++ data.desc = "fake"; ++ data.name = "fake"; ++ data.tname = std::string(typeid(int).name()); ++ data.cppType = "int"; ++ data.alias = 'f'; ++ data.wasPassed = false; ++ data.noTranspose = true; ++ data.required = false; ++ data.input = true; ++ data.loaded = false; ++ data.persistent = false; ++ data.value = boost::any(int(0)); ++ ++ std::string type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(double).name()); ++ data.cppType = "double"; ++ data.value = boost::any(double(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(bool).name()); ++ data.cppType = "double"; ++ data.value = boost::any(bool(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(std::string).name()); ++ data.cppType = "std::string"; ++ data.value = boost::any(std::string("")); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: " << "#doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ ++ data.tname = std::string(typeid(arma::mat).name()); ++ data.cppType = "arma::mat"; ++ data.value = boost::any(arma::mat()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Mat).name()); ++ data.cppType = "arma::Mat"; ++ data.value = boost::any(arma::Mat()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::rowvec).name()); ++ data.cppType = "arma::rowvec"; ++ data.value = boost::any(arma::rowvec()); ++ const std::string& rowType = GetPrintableType(data); ++ ++ oss << " - `" << rowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(rowType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Row).name()); ++ data.cppType = "arma::Row"; ++ data.value = boost::any(arma::Row()); ++ const std::string& urowType = GetPrintableType>(data); ++ ++ oss << " - `" << urowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(urowType) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::vec).name()); ++ data.cppType = "arma::vec"; ++ data.value = boost::any(arma::vec()); ++ const std::string& colType = GetPrintableType(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (colType != rowType) ++ { ++ oss << " - `" << colType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(colType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ } ++ ++ data.tname = std::string(typeid(arma::Col).name()); ++ data.cppType = "arma::Col"; ++ data.value = boost::any(arma::Col()); ++ const std::string& ucolType = GetPrintableType>(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (ucolType != urowType) ++ { ++ oss << " - `" << ucolType << "`{ #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(ucolType) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ } ++ ++ data.tname = ++ std::string(typeid(std::tuple).name()); ++ data.cppType = "std::tuple"; ++ data.value = boost::any(std::tuple()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(priv::mlpackModel).name()); ++ data.cppType = "mlpackModel"; ++ data.value = boost::any(new priv::mlpackModel()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() ++ << "_model }: " << PrintTypeDoc(data) << std::endl; ++ ++ // Clean up memory. ++ delete boost::any_cast(data.value); ++ ++ oss << std::endl << "
    " << std::endl; ++ ++ return oss.str(); ++} ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintValue(value, quotes); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintValue(value, quotes); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter" + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::ostringstream oss; ++ ++ if (d.required) ++ { ++ oss << "**--**"; ++ } ++ else ++ { ++ if (BindingInfo::Language() == "cli") ++ { ++ oss << cli::PrintDefault(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ oss << python::PrintDefault(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDefault: unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ } ++ ++ return oss.str(); ++} ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintDataset(dataset); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintDataset(dataset); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDataset(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Print a model type parameter. ++ */ ++inline std::string PrintModel(const std::string& model) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintModel(model); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintModel(model); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintModel(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ s += cli::ProgramCall(programName, args...); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ s += python::ProgramCall(programName, args...); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```"; ++ return s; ++} ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += "$ " + import + "\n"; ++ s += cli::ProgramCall(programName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += ">>> " + import + "\n"; ++ s += python::ProgramCall(programName); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```\n"; ++ return s; ++} ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName) ++{ ++ // These functions always put a '' around the parameter, so we will skip that ++ // bit. ++ std::string s; ++ if (BindingInfo::Language() == "cli") ++ { ++ // The CLI bindings put a '' around the parameter, so skip that... ++ s = cli::ParamString(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s = python::ParamString(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("ParamString(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + s.substr(1, s.size() - 2) + "`"; ++} ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d) ++{ ++ std::string output; ++ CLI::GetSingleton().functionMap[d.tname]["GetPrintableType"](d, NULL, ++ &output); ++ // We want to make this a link to the type documentation. ++ std::string anchorType = output; ++ bool result; ++ CLI::GetSingleton().functionMap[d.tname]["IsSerializable"](d, NULL, &result); ++ if (result) ++ anchorType = "model"; ++ ++ return "[`" + output + "`](#doc_" + BindingInfo::Language() + "_" + ++ ToUnderscores(anchorType) + ")"; ++} ++ ++template ++inline bool IgnoreCheck(const T& t) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::IgnoreCheck(t); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::IgnoreCheck(t); ++ } ++ else ++ { ++ throw std::invalid_argument("IgnoreCheck(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_docs.cpp b/src/mlpack/bindings/markdown/print_docs.cpp +new file mode 100644 +index 000000000..2711a8789 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.cpp +@@ -0,0 +1,218 @@ ++/** ++ * @file print_docs.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of functions to print Markdown from documentation. ++ */ ++#include "print_docs.hpp" ++ ++#include ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++ ++// Make sure that this is defined. ++#ifndef DOXYGEN_PREFIX ++#define DOXYGEN_PREFIX "https://mlpack.org/docs/mlpack-git/doxygen/" ++#endif ++ ++using namespace std; ++using namespace mlpack; ++using namespace mlpack::util; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages) ++{ ++ // We just want to print the name of the function and a link, as Markdown. ++ // We have to mark it as having the right language with a div. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << " - [" << GetBindingName(bindingName) << "](#" << languages[i] ++ << "_" << bindingName << "){: .language-link #" << languages[i] << " }" ++ << endl; ++ } ++} ++ ++void PrintDocs(const std::string& bindingName, ++ const vector& languages) ++{ ++ ProgramDoc& programDoc = BindingInfo::GetProgramDoc(bindingName); ++ ++ CLI::RestoreSettings(bindingName); ++ ++ // First, for this section, print each of the names. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << "## " << GetBindingName(bindingName) << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName << " }" << endl; ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ // Next we want to print the logical name of the binding (that's known by ++ // ProgramInfo). ++ cout << "#### " << programDoc.programName << endl; ++ cout << endl; ++ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << ProgramCall(bindingName); ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ cout << programDoc.shortDocumentation << " "; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "[Detailed documentation](#" << languages[i] << "_" ++ << bindingName << "_detailed-documentation){: .language-detail-link #" ++ << languages[i] << " }"; ++ } ++ cout << "." << endl; ++ ++ // Next, print the PROGRAM_INFO() documentation for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ // This works with the Kramdown processor. ++ cout << "
    " << endl; ++ ++ // We need to print the signature. ++ ++ // Now, iterate through each of the input options. ++ cout << endl; ++ cout << "### Input options" << endl; ++ cout << endl; ++ ++ cout << "| ***name*** | ***type*** | ***description*** | ***default*** |" ++ << endl; ++ cout << "|------------|------------|-------------------|---------------|" ++ << endl; ++ map& parameters = CLI::Parameters(); ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ continue; ++ ++ // There are some special options that don't exist in some languages. ++ if (languages[i] != "python" && it->second.name == "copy_all_inputs") ++ continue; ++ if (languages[i] != "cli" && ++ (it->second.name == "help" || it->second.name == "info" || ++ it->second.name == "version")) ++ continue; ++ ++ // Print name, type, description, default. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; // just a string ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " | "; ++ string def = PrintDefault(it->second.name); ++ if (def.size() > 0) ++ cout << "`" << def << "` |"; ++ else ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ // Next, iterate through the list of output options. ++ cout << "### Output options" << endl; ++ cout << endl; ++ string outputInfo = PrintOutputOptionInfo(); ++ if (outputInfo.size() > 0) ++ cout << outputInfo << endl; ++ cout << endl; ++ cout << "| ***name*** | ***type*** | ***description*** |" << endl; ++ cout << "|------------|------------|-------------------|" << endl; ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print name, type, description. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ cout << "### Detailed documentation" << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName ++ << "_detailed-documentation }" << endl; ++ cout << endl; ++ cout << programDoc.documentation() << endl; ++ cout << endl; ++ ++ cout << "### See also" << endl; ++ cout << endl; ++ for (size_t j = 0; j < programDoc.seeAlso.size(); ++j) ++ { ++ cout << " - " << "["; ++ // We need special processing if the user has specified a binding name ++ // starting with @ (i.e., '@kfn' or similar). ++ if (programDoc.seeAlso[j].first[0] == '@') ++ cout << GetBindingName(programDoc.seeAlso[j].first.substr(1)); ++ else ++ cout << programDoc.seeAlso[j].first; ++ cout << "]("; ++ ++ // We need special handling of Doxygen information. ++ if (programDoc.seeAlso[j].second.substr(0, 8) == "@doxygen") ++ { ++ cout << DOXYGEN_PREFIX << programDoc.seeAlso[j].second.substr(9); ++ } ++ else if (programDoc.seeAlso[j].second[0] == '#') ++ { ++ cout << "#" << languages[i] << "_" ++ << programDoc.seeAlso[j].second.substr(1); ++ } ++ else ++ { ++ cout << programDoc.seeAlso[j].second; ++ } ++ ++ cout << ")" << endl; ++ } ++ cout << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ } ++ ++ CLI::ClearSettings(); ++} +diff --git a/src/mlpack/bindings/markdown/print_docs.hpp b/src/mlpack/bindings/markdown/print_docs.hpp +new file mode 100644 +index 000000000..abbc88398 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.hpp +@@ -0,0 +1,33 @@ ++/** ++ * @file print_docs.hpp ++ * @author Ryan Curtin ++ * ++ * Functions to generate Markdown for documentation of bindings. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++ ++#include ++ ++/** ++ * Given the current settings of CLI, print the header (which will be the ++ * navigation tab) for the binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ */ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages); ++ ++/** ++ * Given the current settings of CLI, print Markdown documentation for the ++ * binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ * ++ * @param bindingName The binding name (${BINDING} from CMake). ++ * @param languages The set of languages to print documentation for. ++ */ ++void PrintDocs(const std::string& bindingName, ++ const std::vector& languages); ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_type_doc.hpp b/src/mlpack/bindings/markdown/print_type_doc.hpp +new file mode 100644 +index 000000000..108461ed5 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_type_doc.hpp +@@ -0,0 +1,46 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, depending on the current language (as ++ * set in BindingInfo). ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++std::string PrintTypeDoc(const util::ParamData& data) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintTypeDoc::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintTypeDoc::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintTypeDoc(): unknown " ++ "BindingInfo::Language()" + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/program_doc_wrapper.hpp b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +new file mode 100644 +index 000000000..21da3ffc0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +@@ -0,0 +1,41 @@ ++/** ++ * @file program_doc_wrapper.hpp ++ * @author Ryan Curtin ++ * ++ * A simple wrapper around ProgramDoc that also calls ++ * BindingInfo::RegisterProgramDoc() upon construction. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++ ++#include "binding_info.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++class ProgramDocWrapper ++{ ++ public: ++ /** ++ * Construct a ProgramDoc object and register it with ++ * BindingInfo::RegisterProgramDoc(). ++ */ ++ ProgramDocWrapper(const std::string& bindingName, ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& ++ seeAlso) ++ { ++ util::ProgramDoc pd(programName, shortDocumentation, documentation, ++ seeAlso); ++ BindingInfo::RegisterProgramDoc(bindingName, pd); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/res/change_language.js b/src/mlpack/bindings/markdown/res/change_language.js +new file mode 100644 +index 000000000..4b520205f +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/change_language.js +@@ -0,0 +1,102 @@ ++/** ++ * A utility function to change the language displayed on the page. This ++ * function should be called whenever the language is changed from the ++ * drop-down. ++ */ ++function changeLanguage() ++{ ++ lang = document.getElementById("language-select").value; ++ var links = document.getElementsByClassName("language-link"); ++ for (i = 0; i < links.length; ++i) ++ { ++ // With each of the links, we get the inner
    , but we need the parent ++ //
  • . ++ if (links[i].id == lang || links[i].id == "always") ++ links[i].parentElement.style.display = "list-item"; ++ else ++ links[i].parentElement.style.display = "none"; ++ } ++ ++ var titles = document.getElementsByClassName("language-title"); ++ for (i = 0; i < titles.length; ++i) ++ { ++ if (titles[i].id == lang) ++ titles[i].style.display = "inline"; ++ else ++ titles[i].style.display = "none"; ++ } ++ ++ var headers = document.getElementsByClassName("language-header"); ++ for (i = 0; i < headers.length; ++i) ++ { ++ if (headers[i].id == lang) ++ headers[i].style.display = "inline"; ++ else ++ headers[i].style.display = "none"; ++ } ++ ++ var decls = document.getElementsByClassName("language-decl"); ++ for (i = 0; i < decls.length; ++i) ++ { ++ if (decls[i].id == lang) ++ decls[i].style.display = "inline"; ++ else ++ decls[i].style.display = "none"; ++ } ++ ++ var types = document.getElementsByClassName("language-types"); ++ for (i = 0; i < types.length; ++i) ++ { ++ if (types[i].id == lang) ++ types[i].style.display = "inline"; ++ else ++ types[i].style.display = "none"; ++ } ++ ++ var details = document.getElementsByClassName("language-detail-link"); ++ for (i = 0; i < details.length; ++i) ++ { ++ if (details[i].id == lang) ++ details[i].style.display = "inline"; ++ else ++ details[i].style.display = "none"; ++ } ++ ++ var sections = document.getElementsByClassName("language-section"); ++ for (i = 0; i < sections.length; ++i) ++ { ++ if (sections[i].id == lang) ++ sections[i].style.display = "inline"; ++ else ++ sections[i].style.display = "none"; ++ } ++} ++ ++document.body.onload = function() ++{ ++ // Do we need to manually set the language because the user came with an ++ // anchor? ++ if (window.location.hash) ++ { ++ // Try to extract the language. ++ firstUnderscore = window.location.hash.indexOf("_"); ++ if (firstUnderscore !== -1) ++ { ++ var lang = window.location.hash.substring(1, firstUnderscore); ++ // Now see if it's in the list of languages. ++ var select = document.getElementById("language-select"); ++ for (i = 0; i < select.length; ++i) ++ { ++ var select_lang = select[i].value; ++ // Is the language a match? ++ if (lang === select_lang) ++ { ++ select.value = select_lang; ++ break; ++ } ++ } ++ } ++ } ++ ++ changeLanguage(); ++} +diff --git a/src/mlpack/bindings/markdown/res/formatting.css b/src/mlpack/bindings/markdown/res/formatting.css +new file mode 100644 +index 000000000..487b026e7 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/formatting.css +@@ -0,0 +1,142 @@ ++body ++{ ++ background: #000000; ++} ++ ++/* Don't display other languages by default. */ ++div.language-title, div.language-header, div.language-decl, ++div.language-detail-link, div.language-types, div.language-section ++{ ++ display: none; ++} ++ ++/* Just display cli documentation. */ ++div.language-title#cli, div.language-header#cli, div.language-decl#cli, ++div.language-detail-link#cli, div.language-types#cli, div.language-section#cli ++{ ++ display: none; ++} ++ ++div#header ++{ ++ padding-top: 10px; ++ width: 200px; ++ background: #000000; ++ border-right: 2px solid #333333; ++ height: 100%; ++ position: fixed; ++ z-index: 1; ++ top: 0; ++ left: 0; ++ overflow-y: scroll; ++} ++ ++div#header .language-select-div select ++{ ++ color: #ffffff; ++ background: transparent; ++ border: none; ++ height: 29px; ++ padding: 5px; ++ width: 190px; ++} ++ ++div#header .language-select-div ++{ ++ color: #ffffff; ++ border: 2px solid #333333; ++ background: url('../res/menu_bg.png') no-repeat 96% 0; ++ height: 34px; ++ width: 180px; ++ overflow: hidden; ++ margin-bottom: 20px; ++ ++ -webkit-border-radius: 5px; ++ -moz-border-radius: 5px; ++ border-radius: 5px; ++} ++ ++div#header ul ++{ ++ padding-top: 5px; ++ margin-bottom: 5px; ++ color: #bb2000; ++ display: table; ++ text-align: left; ++ padding-left: 0px; ++ margin-left: 15px; ++ font-size: 75%; ++} ++ ++div#header ul li ++{ ++ line-height: 1.3em; ++} ++ ++div#header ul li a ++{ ++ color: #eab72c; ++ font-weight: none; ++} ++ ++div#header ul li a:hover ++{ ++ color: #ffffff; ++ font-weight: none; ++} ++ ++div#docs ++{ ++ margin-left: 200px; ++} ++ ++span.special ++{ ++ font-size: 85%; ++ color: #eab72c; ++} ++ ++div.category ++{ ++ text-align: left; ++} ++ ++div.category h5 ++{ ++ text-align: left; ++ margin-top: 0; ++ margin-bottom: 0; ++ color: #ffffff; ++ font-size: 100%; ++ font-weight: normal; ++ padding: 0; ++} ++ ++h2#mlpack-overview, h2.language-types-h2 ++{ ++ padding-bottom: 0.75em; ++} ++ ++div.language-header h1 ++{ ++ color: #ffffff; ++ text-align: center; ++ border-top: none; ++ border-bottom: 1px solid #eab72c; ++} ++ ++div.language-types li code ++{ ++ font-weight: bold; ++ color: #eab72c; ++} ++ ++div.language-types li ++{ ++ padding-bottom: 1em; ++} ++ ++div.language-section table td a code:hover ++{ ++ color: #bb2000; ++} +diff --git a/src/mlpack/bindings/markdown/res/menu_bg.png b/src/mlpack/bindings/markdown/res/menu_bg.png +new file mode 100644 +index 0000000000000000000000000000000000000000..c29580a400267755c0f2a7d1b8f154d329797553 +GIT binary patch +literal 395 +zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I +zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@$z$e7@|Ns9$rm%z} +zkO5&YUc4A6vSP&wAh~+=Y9P6B<3=C}Q4qtiwFt=JD+%%oW?-oBD6-sRv)}%J<6##S +z{uAOr8O9`UcNcz%T?{vY9QG1VUsv|Wj9gsIB1StofUaP$^K@|xk+_^3kmk^E@KBlv +z3nQb?wiDJ01q~ZqKi}J1EyfTU{PA6^wa?xsyTXMZR(+r2Hbc2uaP~}(9I0R241Yd| +zZn0k0^$2K^YKdz^NlIc#s#S7PDv)9@GBC8%H89jQGzc*?wK6caGBVIL05S}G9 ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. This is for regular types. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a matrix option, a tuple option, a ++ * serializable option, or a string option (this returns the default filename, ++ * or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */ = 0); ++ ++/** ++ * Return the default value of a model option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of an option. This is the function that will be ++ * placed into the CLI functionMap. ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ std::string* outstr = (std::string*) output; ++ *outstr = DefaultParamImpl::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "default_param_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/default_param_impl.hpp b/src/mlpack/bindings/python/default_param_impl.hpp +new file mode 100644 +index 000000000..bae57cce2 +--- /dev/null ++++ b/src/mlpack/bindings/python/default_param_impl.hpp +@@ -0,0 +1,147 @@ ++/** ++ * @file default_param_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the default value of a parameter, depending on its type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++ ++#include "default_param.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type* /* junk */) ++{ ++ std::ostringstream oss; ++ if (std::is_same::value) ++ oss << "False"; ++ else ++ oss << boost::any_cast(data.value); ++ ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ // Print each element in an array delimited by square brackets. ++ std::ostringstream oss; ++ const T& vector = boost::any_cast(data.value); ++ oss << "["; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ const std::string& s = *boost::any_cast(&data.value); ++ return "'" + s + "'"; ++} ++ ++/** ++ * Return the default value of a matrix option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */) ++{ ++ // Get the filename and return it, or return an empty string. ++ if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "np.empty([0])"; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "np.empty([0], dtype=np.uint64)"; ++ } ++ else if (std::is_same>::value) ++ { ++ return "np.empty([0, 0], dtype=np.uint64)"; ++ } ++ else ++ { ++ return "np.empty([0, 0])"; ++ } ++} ++ ++/** ++ * Return the default value of a model option (always "None"). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ return "None"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_cython_type.hpp b/src/mlpack/bindings/python/get_cython_type.hpp +index e44f4f70b..e44b91eb0 100644 +--- a/src/mlpack/bindings/python/get_cython_type.hpp ++++ b/src/mlpack/bindings/python/get_cython_type.hpp +@@ -40,16 +40,6 @@ inline std::string GetCythonType( + return "int"; + } + +-template<> +-inline std::string GetCythonType( +- const util::ParamData& /* d */, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*) +-{ +- return "float"; +-} +- + template<> + inline std::string GetCythonType( + const util::ParamData& /* d */, +diff --git a/src/mlpack/bindings/python/get_printable_type.hpp b/src/mlpack/bindings/python/get_printable_type.hpp +new file mode 100644 +index 000000000..51a528b4d +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type.hpp +@@ -0,0 +1,120 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++void GetPrintableType(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(d); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_printable_type_impl.hpp b/src/mlpack/bindings/python/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..2031b3435 +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type_impl.hpp +@@ -0,0 +1,151 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "unknown"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "int"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "double"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "string"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "size_t"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "bool"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "list of " + GetPrintableType(d) + "s"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ std::string type = "matrix"; ++ if (std::is_same::value) ++ { ++ if (T::is_row || T::is_col) ++ type = "vector"; ++ } ++ else if (std::is_same::value) ++ { ++ type = "int matrix"; ++ if (T::is_row || T::is_col) ++ type = "int vector"; ++ } ++ ++ return type; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type*) ++{ ++ return "categorical matrix"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return d.cppType + "Type"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_doc.hpp b/src/mlpack/bindings/python/print_doc.hpp +index 7a5a27781..b8962ccaf 100644 +--- a/src/mlpack/bindings/python/print_doc.hpp ++++ b/src/mlpack/bindings/python/print_doc.hpp +@@ -14,7 +14,7 @@ + + #include + #include +-#include "get_python_type.hpp" ++#include "get_printable_type.hpp" + + namespace mlpack { + namespace bindings { +@@ -44,24 +44,20 @@ void PrintDoc(const util::ParamData& d, + oss << d.name << "_ ("; + else + oss << d.name << " ("; +- oss << GetPythonType::type>(d) << "): " ++ oss << GetPrintableType::type>(d) << "): " + << d.desc; + + // Print a default, if possible. + if (!d.required) + { +- if (d.cppType == "std::string") ++ // Call the correct overload to get the default value directly. ++ if (d.cppType == "std::string" || d.cppType == "double" || ++ d.cppType == "int" || d.cppType == "std::vector" || ++ d.cppType == "std::vector" || ++ d.cppType == "std::vector") + { +- oss << " Default value '" << boost::any_cast(d.value) +- << "'."; +- } +- else if (d.cppType == "double") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; +- } +- else if (d.cppType == "int") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; ++ std::string defaultValue = DefaultParamImpl(d); ++ oss << " Default value " << defaultValue << "."; + } + } + +diff --git a/src/mlpack/bindings/python/print_doc_functions.hpp b/src/mlpack/bindings/python/print_doc_functions.hpp +index a9207c570..51e85b78d 100644 +--- a/src/mlpack/bindings/python/print_doc_functions.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions.hpp +@@ -19,6 +19,21 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -29,6 +44,11 @@ inline std::string PrintValue(const T& value, bool quotes); + template<> + inline std::string PrintValue(const bool& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + // Recursion base case. + inline std::string PrintInputOptions(); + +@@ -57,6 +77,12 @@ std::string PrintOutputOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +diff --git a/src/mlpack/bindings/python/print_doc_functions_impl.hpp b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +index c27e88609..0de6a5312 100644 +--- a/src/mlpack/bindings/python/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +@@ -19,6 +19,32 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ // No modification is needed to the name---we just use it as-is. ++ return bindingName + "()"; ++} ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ return "from mlpack import " + bindingName; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return "Results are returned in a Python dictionary. The keys of the " ++ "dictionary are the names of the output parameters."; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -48,6 +74,23 @@ inline std::string PrintValue(const bool& value, bool quotes) + return "False"; + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + // Recursion base case. + std::string PrintInputOptions() { return ""; } + +@@ -136,7 +179,8 @@ std::string PrintOutputOptions(const std::string& paramName, + + /** + * Given a name of a binding and a variable number of arguments (and their +- * contents), print the corresponding function call. ++ * contents), print the corresponding function call. The given programName ++ * should not be the output of GetBindingName(). + */ + template + std::string ProgramCall(const std::string& programName, Args... args) +@@ -166,6 +210,76 @@ std::string ProgramCall(const std::string& programName, Args... args) + return util::HyphenateString(call, 2) + "\n" + oss.str(); + } + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. The programName should not be the output of GetBindingName(). ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << ">>> "; ++ ++ // Determine if we have any output options. ++ const std::map& parameters = CLI::Parameters(); ++ bool hasOutput = false; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ { ++ hasOutput = true; ++ break; ++ } ++ } ++ ++ if (hasOutput) ++ oss << "d = "; ++ ++ oss << programName << "("; ++ ++ // Now iterate over every input option. ++ bool first = true; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ if (!first) ++ oss << ", "; ++ else ++ first = false; ++ ++ // Print the input option. ++ if (it->second.name != "lambda") // Don't print Python keywords. ++ oss << it->second.name << "="; ++ else ++ oss << it->second.name << "_="; ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ oss << value; ++ } ++ oss << ")"; ++ ++ std::string result = util::HyphenateString(oss.str(), 8); ++ ++ oss.str(""); ++ oss << result; ++ ++ // Now print output lines. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print a new line for the output option. ++ oss << std::endl << ">>> " << it->second.name << " = d['" ++ << it->second.name << "']"; ++ } ++ ++ return oss.str(); ++} ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +@@ -183,14 +297,6 @@ inline std::string PrintDataset(const std::string& datasetName) + return "'" + datasetName + "'"; + } + +-/** +- * Given the name of a binding, print its invocation. +- */ +-inline std::string ProgramCall(const std::string& programName) +-{ +- return ">>> " + programName + "("; +-} +- + /** + * Print any closing call to a program. For a Python binding this is a closing + * brace. +diff --git a/src/mlpack/bindings/python/print_input_processing.hpp b/src/mlpack/bindings/python/print_input_processing.hpp +index e271906cb..04f318a7c 100644 +--- a/src/mlpack/bindings/python/print_input_processing.hpp ++++ b/src/mlpack/bindings/python/print_input_processing.hpp +@@ -17,7 +17,6 @@ + #include "get_numpy_type.hpp" + #include "get_numpy_type_char.hpp" + #include "get_cython_type.hpp" +-#include "get_python_type.hpp" + #include "strip_type.hpp" + + namespace mlpack { +diff --git a/src/mlpack/bindings/python/print_type_doc.hpp b/src/mlpack/bindings/python/print_type_doc.hpp +new file mode 100644 +index 000000000..9a5d6dc71 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_type_doc_impl.hpp b/src/mlpack/bindings/python/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..8d6ae3971 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc_impl.hpp +@@ -0,0 +1,162 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option (True or False)."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A list of integers; i.e., [0, 1, 2]."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A list of strings; i.e., [\"hello\", \"goodbye\"]."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data. This can be a 2-d matrix where " ++ "one dimension has size 1, or it can also be a list, a numpy 1-d " ++ "ndarray, or a 1-d pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data. This can be a list of lists, a " ++ "numpy ndarray, or a pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ } ++ else if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data with a uint64 dtype. This can be" ++ " a 2-d matrix where one dimension has size 1, or it can also be a " ++ "list, a numpy 1-d ndarray, or a 1-d pandas DataFrame. If the dtype " ++ "is not already uint64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data with a uint64 dtype. This can " ++ "be a list of lists, a numpy ndarray, or a pandas DataFrame. If the " ++ "dtype is not already uint64, it will be converted."; ++ } ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A 2-d arraylike containing data. Like the regular 2-d matrices, this" ++ " can be a list of lists, a numpy ndarray, or a pandas DataFrame. " ++ "However, this type can also accept a pandas DataFrame that has columns " ++ "of type 'CategoricalDtype'. These categorical values will be converted " ++ "to numeric indices before being passed to mlpack, and then inside mlpack" ++ " they will be properly treated as categorical variables, so there is no " ++ "need to do one-hot encoding for this matrix type. If the dtype of the " ++ "given matrix is not already float64, it will be converted."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "An mlpack model pointer. This type can be pickled to or from disk, " ++ "and internally holds a pointer to C++ memory containing the mlpack " ++ "model. Note that this means that the mlpack model itself cannot be " ++ "easily inspected in Python; however, the pickled model can be loaded " ++ "in C++ and inspected there."; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/py_option.hpp b/src/mlpack/bindings/python/py_option.hpp +index 98bba2dd5..e175d282b 100644 +--- a/src/mlpack/bindings/python/py_option.hpp ++++ b/src/mlpack/bindings/python/py_option.hpp +@@ -13,6 +13,7 @@ + #define MLPACK_BINDINGS_PYTHON_PY_OPTION_HPP + + #include ++#include "default_param.hpp" + #include "get_param.hpp" + #include "get_printable_param.hpp" + #include "print_class_defn.hpp" +@@ -85,6 +86,9 @@ class PyOption + CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = + &GetPrintableParam; + ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ + // These are used by the pyx generator. + CLI::GetSingleton().functionMap[data.tname]["PrintClassDefn"] = + &PrintClassDefn; +diff --git a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +index cf3c1055d..93daaddc4 100644 +--- a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp ++++ b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +@@ -19,6 +19,7 @@ using namespace mlpack; + using namespace mlpack::kernel; + + PROGRAM_INFO("Python binding test", ++ "A simple program to test Python binding functionality.", + "A simple program to test Python binding functionality. You can build " + "mlpack with the BUILD_TESTS option set to off, and this binding will " + "no longer be built."); +diff --git a/src/mlpack/core/util/cli.cpp b/src/mlpack/core/util/cli.cpp +index f2bf6cec2..1a83080bd 100644 +--- a/src/mlpack/core/util/cli.cpp ++++ b/src/mlpack/core/util/cli.cpp +@@ -27,7 +27,8 @@ using namespace mlpack; + using namespace mlpack::util; + + // Fake ProgramDoc in case none is supplied. +-static ProgramDoc emptyProgramDoc = ProgramDoc("", []() { return ""; }); ++static ProgramDoc emptyProgramDoc = ProgramDoc("", "", []() { return ""; }, ++ {}); + + /* Constructors, Destructors, Copy */ + /* Make the constructor private, to preclude unauthorized instances */ +diff --git a/src/mlpack/core/util/mlpack_main.hpp b/src/mlpack/core/util/mlpack_main.hpp +index 4c915c0c0..8003f38ac 100644 +--- a/src/mlpack/core/util/mlpack_main.hpp ++++ b/src/mlpack/core/util/mlpack_main.hpp +@@ -21,6 +21,7 @@ + #define BINDING_TYPE_CLI 0 + #define BINDING_TYPE_TEST 1 + #define BINDING_TYPE_PYX 2 ++#define BINDING_TYPE_MARKDOWN 128 + #define BINDING_TYPE_UNKNOWN -1 + + #ifndef BINDING_TYPE +@@ -99,9 +100,10 @@ using Option = mlpack::bindings::tests::TestOption; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }) + + #elif(BINDING_TYPE == BINDING_TYPE_PYX) // This is a Python binding. + +@@ -128,9 +130,10 @@ static const std::string testName = ""; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); \ ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }); \ + namespace mlpack { \ + namespace bindings { \ + namespace python { \ +@@ -148,6 +151,59 @@ PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" + + // Nothing else needs to be defined---the binding will use mlpackMain() as-is. + ++#elif BINDING_TYPE == BINDING_TYPE_MARKDOWN ++ ++// It doesn't really matter whether this is true or false... ++#define BINDING_MATRIX_TRANSPOSED true ++ ++// We use BINDING_NAME in PROGRAM_INFO() so it needs to be defined. ++#ifndef BINDING_NAME ++ #error "BINDING_NAME must be defined when BINDING_TYPE is Markdown!" ++#endif ++ ++#include ++#include ++ ++#define PRINT_PARAM_STRING mlpack::bindings::markdown::ParamString ++#define PRINT_PARAM_VALUE mlpack::bindings::markdown::PrintValue ++#define PRINT_DATASET mlpack::bindings::markdown::PrintDataset ++#define PRINT_MODEL mlpack::bindings::markdown::PrintModel ++#define PRINT_CALL mlpack::bindings::markdown::ProgramCall ++#define BINDING_IGNORE_CHECK mlpack::bindings::markdown::IgnoreCheck ++ ++namespace mlpack { ++namespace util { ++ ++template ++using Option = mlpack::bindings::markdown::MDOption; ++ ++} ++} ++ ++#include ++#include ++ ++#undef PROGRAM_INFO ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) static \ ++ mlpack::bindings::markdown::ProgramDocWrapper \ ++ cli_programdoc_dummy_object = \ ++ mlpack::bindings::markdown::ProgramDocWrapper(BINDING_NAME, NAME, \ ++ SHORT_DESC, []() { return DESC; }, { __VA_ARGS__ }); \ ++ ++PARAM_FLAG("verbose", "Display informational messages and the full list of " ++ "parameters and timers at the end of execution.", "v"); ++ ++// CLI-specific parameters. ++PARAM_FLAG("help", "Default help info.", "h"); ++PARAM_STRING_IN("info", "Print help on a specific option.", "", ""); ++PARAM_FLAG("version", "Display the version of mlpack.", "V"); ++ ++// Python-specific parameters. ++PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" ++ " copied before the method is run. This is useful for debugging problems " ++ "where the input parameters are being modified by the algorithm, but can " ++ "slow down the code.", ""); ++ + #else + + #error "Unknown binding type! Be sure BINDING_TYPE is defined if you are " \ +diff --git a/src/mlpack/core/util/param.hpp b/src/mlpack/core/util/param.hpp +index b812887fb..de50f5b00 100644 +--- a/src/mlpack/core/util/param.hpp ++++ b/src/mlpack/core/util/param.hpp +@@ -29,6 +29,21 @@ using DatasetInfo = DatasetMapper; + } // namespace data + } // namespace mlpack + ++/** ++ * Provide a link for a binding's "see also" documentation section, which is ++ * primarily (but not necessarily exclusively) used by the Markdown bindings ++ * This link can be specified by calling SEE_ALSO("description", "link"), where ++ * "description" is the description of the link and "link" may be one of the ++ * following: ++ * ++ * - A direct URL, starting with http:// or https://. ++ * - A page anchor for documentation, referencing another binding by its CMake ++ * binding name, i.e. "#knn". ++ * - A link to a Doxygen page, using the mangled Doxygen name after a ++ * '@doxygen/', i.e., "@doxygen/mlpack1_1_adaboost1_1_AdaBoost". ++ */ ++#define SEE_ALSO(DESCRIPTION, LINK) {DESCRIPTION, LINK} ++ + /** + * Document an executable. Only one instance of this macro should be + * present in your program! Therefore, use it in the main.cpp +@@ -41,14 +56,22 @@ using DatasetInfo = DatasetMapper; + * PARAM_DOUBLE_OUT_REQ(), PARAM_VECTOR_OUT_REQ(), PARAM_STRING_OUT_REQ(). + * + * @param NAME Short string representing the name of the program. ++ * @param SHORT_DESC Short two-sentence description of the program; it should ++ * describe what the program implements and does, and a quick overview of ++ * how it can be used and what it should be used for. + * @param DESC Long string describing what the program does and possibly a + * simple usage example. Newlines should not be used here; this is taken + * care of by CLI (however, you can explicitly specify newlines to denote +- * new paragraphs). ++ * new paragraphs). You can also use printing macros like ++ * PRINT_PARAM_STRING(), PRINT_DATASET(), and others. ++ * @param SEE_ALSOS A set of SEE_ALSO() macros that are used for generating ++ * documentation. See the SEE_ALSO() macro. This is a varargs argument, so ++ * you can add as many SEE_ALSO()s as you like. + */ +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }) ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ } ) + + /** + * Define a flag parameter. +diff --git a/src/mlpack/core/util/program_doc.cpp b/src/mlpack/core/util/program_doc.cpp +index 814573b1c..28d7ee8b8 100644 +--- a/src/mlpack/core/util/program_doc.cpp ++++ b/src/mlpack/core/util/program_doc.cpp +@@ -23,17 +23,33 @@ using namespace std; + * Construct a ProgramDoc object. When constructed, it will register itself + * with CLI. A fatal error will be thrown if more than one is constructed. + * +- * @param programName Short string representing the name of the program. +- * @param documentation Long string containing documentation on how to use the +- * program and what it is. No newline characters are necessary; this is +- * taken care of by CLI later. + * @param defaultModule Name of the default module. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. ++ * @param documentation Long string containing documentation on how to use the ++ * program and what it is. No newline characters are necessary; this is ++ * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ +-ProgramDoc::ProgramDoc(const std::string& programName, +- const std::function& documentation) : ++ProgramDoc::ProgramDoc( ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso) : + programName(programName), +- documentation(documentation) ++ shortDocumentation(shortDocumentation), ++ documentation(documentation), ++ seeAlso(seeAlso) + { + // Register this with CLI. + CLI::RegisterProgramDoc(this); + } ++ ++/** ++ * Construct an empty ProgramDoc object. ++ */ ++ProgramDoc::ProgramDoc() ++{ ++ CLI::RegisterProgramDoc(this); ++} +diff --git a/src/mlpack/core/util/program_doc.hpp b/src/mlpack/core/util/program_doc.hpp +index f5de89fe5..296b15586 100644 +--- a/src/mlpack/core/util/program_doc.hpp ++++ b/src/mlpack/core/util/program_doc.hpp +@@ -33,17 +33,32 @@ class ProgramDoc + * will be returned. + * + * @param programName Short string representing the name of the program. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. + * @param documentation Long string containing documentation on how to use the + * program and what it is. No newline characters are necessary; this is + * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ + ProgramDoc(const std::string& programName, +- const std::function& documentation); ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso); ++ ++ /** ++ * Construct an empty ProgramDoc object. (This is not meant to be used!) ++ */ ++ ProgramDoc(); + + //! The name of the program. + std::string programName; ++ //! The short documentation for the program. ++ std::string shortDocumentation; + //! Documentation for what the program does. + std::function documentation; ++ //! Set of see also information. ++ std::vector> seeAlso; + }; + + } // namespace util +diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt +index 4e6fc3df9..360e79f93 100644 +--- a/src/mlpack/methods/CMakeLists.txt ++++ b/src/mlpack/methods/CMakeLists.txt +@@ -74,3 +74,7 @@ endforeach() + + set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) ++set(MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++set(MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++set(MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) +diff --git a/src/mlpack/methods/adaboost/CMakeLists.txt b/src/mlpack/methods/adaboost/CMakeLists.txt +index 5505f057f..3e85237e0 100644 +--- a/src/mlpack/methods/adaboost/CMakeLists.txt ++++ b/src/mlpack/methods/adaboost/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(adaboost) + add_python_binding(adaboost) ++add_markdown_docs(adaboost "cli;python" "classification") +diff --git a/src/mlpack/methods/adaboost/adaboost_main.cpp b/src/mlpack/methods/adaboost/adaboost_main.cpp +index 0c3be12e2..ced3d3c1f 100644 +--- a/src/mlpack/methods/adaboost/adaboost_main.cpp ++++ b/src/mlpack/methods/adaboost/adaboost_main.cpp +@@ -46,7 +46,14 @@ using namespace mlpack::decision_stump; + using namespace mlpack::perceptron; + using namespace mlpack::util; + +-PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " ++PROGRAM_INFO("AdaBoost", ++ // Short description. ++ "An implementation of the AdaBoost.MH (Adaptive Boosting) algorithm for " ++ "classification. This can be used to train an AdaBoost model on labeled " ++ "data or use an existing AdaBoost model to predict the classes of new " ++ "points.", ++ // Long description. ++ "This program implements the AdaBoost (or Adaptive " + "Boosting) algorithm. The variant of AdaBoost implemented here is " + "AdaBoost.MH. It uses a weak learner, either decision stumps or " + "perceptrons, and over many iterations, creates a strong learner that is a " +@@ -72,7 +79,7 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + "classes for each point in the test dataset are output to the " + + PRINT_PARAM_STRING("output") + " output parameter. The AdaBoost model " + "itself is output to the " + PRINT_PARAM_STRING("output_model") + +- "output parameter." ++ " output parameter." + "\n\n" + "For example, to run AdaBoost on an input dataset " + + PRINT_DATASET("data") + " with perceptrons as the weak learner type, " +@@ -88,7 +95,15 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + PRINT_DATASET("predictions") + " with the following command: " + "\n\n" + + PRINT_CALL("adaboost", "input_model", "model", "test", "test_data", +- "output", "predictions")); ++ "output", "predictions"), ++ // See also... ++ SEE_ALSO("AdaBoost on Wikipedia", "https://en.wikipedia.org/wiki/AdaBoost"), ++ SEE_ALSO("Improved boosting algorithms using confidence-rated predictions " ++ "(pdf)", "http://rob.schapire.net/papers/SchapireSi98.pdf"), ++ SEE_ALSO("Perceptron", "#perceptron"), ++ SEE_ALSO("Decision Stump", "#decision_stump"), ++ SEE_ALSO("mlpack::adaboost::AdaBoost C++ class documentation", ++ "@doxygen/classmlpack_1_1adaboost_1_1AdaBoost.html")); + + // Input for training. + PARAM_MATRIX_IN("training", "Dataset for training AdaBoost.", "t"); +diff --git a/src/mlpack/methods/approx_kfn/CMakeLists.txt b/src/mlpack/methods/approx_kfn/CMakeLists.txt +index da20af02b..be1a5c4f0 100644 +--- a/src/mlpack/methods/approx_kfn/CMakeLists.txt ++++ b/src/mlpack/methods/approx_kfn/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # This program computes approximate furthest neighbors. + add_cli_executable(approx_kfn) + add_python_binding(approx_kfn) ++add_markdown_docs(approx_kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +index 9adce050e..7091536fd 100644 +--- a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp ++++ b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +@@ -22,6 +22,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Approximate furthest neighbor search", ++ // Short description. ++ "An implementation of two strategies for furthest neighbor search. This " ++ "can be used to compute the furthest neighbor of query point(s) from a set " ++ "of points; furthest neighbor models can be saved and reused with future " ++ "query point(s).", ++ // Long description. + "This program implements two strategies for furthest neighbor search. " + "These strategies are:" + "\n\n" +@@ -86,7 +92,18 @@ PROGRAM_INFO("Approximate furthest neighbor search", + PRINT_DATASET("neighbors") + " by calling" + "\n\n" + + PRINT_CALL("approx_kfn", "input_model", "model", "query", "new_query_set", +- "k", 3, "neighbors", "neighbors")); ++ "k", 3, "neighbors", "neighbors"), ++ SEE_ALSO("k-furthest-neighbor search", "#kfn"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Fast approximate furthest neighbors with data-dependent candidate" ++ " selection (pdf)", "http://ratml.org/pub/pdf/2016fast.pdf"), ++ SEE_ALSO("Approximate furthest neighbor in high dimensions (pdf)", ++ "https://pdfs.semanticscholar.org/a4b5/7b9cbf37201fb1d9a56c0f4eefad0466" ++ "9c20.pdf"), ++ SEE_ALSO("mlpack::neighbor::QDAFN class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1QDAFN.html"), ++ SEE_ALSO("mlpack::neighbor::DrusillaSelect class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1DrusillaSelect.html")); + + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); + PARAM_MATRIX_IN("query", "Matrix containing query points.", "q"); +diff --git a/src/mlpack/methods/cf/CMakeLists.txt b/src/mlpack/methods/cf/CMakeLists.txt +index 01796f29d..976304458 100644 +--- a/src/mlpack/methods/cf/CMakeLists.txt ++++ b/src/mlpack/methods/cf/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(cf) + add_python_binding(cf) ++add_markdown_docs(cf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp +index 0164da62d..28e8f756d 100644 +--- a/src/mlpack/methods/cf/cf_main.cpp ++++ b/src/mlpack/methods/cf/cf_main.cpp +@@ -30,7 +30,13 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " ++PROGRAM_INFO("Collaborative Filtering", ++ // Short description. ++ "An implementation of several collaborative filtering (CF) techniques for " ++ "recommender systems. This can be used to train a new CF model, or use an" ++ " existing CF model to compute recommendations.", ++ // Long description. ++ "This program performs collaborative " + "filtering (CF) on the given dataset. Given a list of user, item and " + "preferences (the " + PRINT_PARAM_STRING("training") + " parameter), " + "the program will perform a matrix decomposition and then can perform a " +@@ -53,7 +59,7 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "addition, the number of recommendations per user to generate can be " + "specified with the " + PRINT_PARAM_STRING("recommendations") + " " + "parameter, and the number of similar users (the size of the neighborhood) " +- " to be considered when generating recommendations can be specified with " ++ "to be considered when generating recommendations can be specified with " + "the " + PRINT_PARAM_STRING("neighborhood") + " parameter." + "\n\n" + "For performing the matrix decomposition, the following optimization " +@@ -83,7 +89,20 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "call " + "\n\n" + + PRINT_CALL("cf", "input_model", "model", "query", "users", +- "recommendations", 5, "output", "recommendations")); ++ "recommendations", 5, "output", "recommendations"), ++ SEE_ALSO("Collaborative filtering tutorial", "@doxygen/cftutorial.html"), ++ SEE_ALSO("Alternating Matrix Factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Collaborative Filtering on Wikipedia", ++ "https://en.wikipedia.org/wiki/Collaborative_filtering"), ++ SEE_ALSO("Matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Matrix_factorization_" ++ "(recommender_systems)"), ++ SEE_ALSO("Matrix factorization techniques for recommender systems (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.441.3234" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::cf::CFType class documentation", ++ "@doxygen/classmlpack_1_1cf_1_1CFType.html")); + + // Parameters for training a model. + PARAM_MATRIX_IN("training", "Input dataset to perform CF on.", "t"); +diff --git a/src/mlpack/methods/dbscan/CMakeLists.txt b/src/mlpack/methods/dbscan/CMakeLists.txt +index 70939c9d0..47b79d782 100644 +--- a/src/mlpack/methods/dbscan/CMakeLists.txt ++++ b/src/mlpack/methods/dbscan/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(dbscan) + add_python_binding(dbscan) ++add_markdown_docs(dbscan "cli;python" "clustering") +diff --git a/src/mlpack/methods/dbscan/dbscan_main.cpp b/src/mlpack/methods/dbscan/dbscan_main.cpp +index c4afc3c85..04bcfc85f 100644 +--- a/src/mlpack/methods/dbscan/dbscan_main.cpp ++++ b/src/mlpack/methods/dbscan/dbscan_main.cpp +@@ -27,6 +27,10 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("DBSCAN clustering", ++ // Short description. ++ "An implementation of DBSCAN clustering. Given a dataset, this can " ++ "compute and return a clustering of that dataset.", ++ // Long description. + "This program implements the DBSCAN algorithm for clustering using " + "accelerated tree-based range search. The type of tree that is used " + "may be parameterized, or brute-force range search may also be used." +@@ -59,7 +63,13 @@ PROGRAM_INFO("DBSCAN clustering", + PRINT_DATASET("input") + " with a radius of 0.5 and a minimum cluster size" + " of 5 is given below:" + "\n\n" + +- PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5)); ++ PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5), ++ SEE_ALSO("DBSCAN on Wikipedia", "https://en.wikipedia.org/wiki/DBSCAN"), ++ SEE_ALSO("A density-based algorithm for discovering clusters in large " ++ "spatial databases with noise (pdf)", ++ "http://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf"), ++ SEE_ALSO("mlpack::dbscan::DBSCAN class documentation", ++ "@doxygen/classmlpack_1_1dbscan_1_1DBSCAN.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to cluster.", "i"); + PARAM_UROW_OUT("assignments", "Output matrix for assignments of each " +diff --git a/src/mlpack/methods/decision_stump/CMakeLists.txt b/src/mlpack/methods/decision_stump/CMakeLists.txt +index e9d9c04c1..a11b0048e 100644 +--- a/src/mlpack/methods/decision_stump/CMakeLists.txt ++++ b/src/mlpack/methods/decision_stump/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_stump) + add_python_binding(decision_stump) ++add_markdown_docs(decision_stump "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_stump/decision_stump_main.cpp b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +index aa4d9a67d..6f8f308af 100644 +--- a/src/mlpack/methods/decision_stump/decision_stump_main.cpp ++++ b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +@@ -22,6 +22,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Decision Stump", ++ // Short description. ++ "An implementation of a decision stump, which is a single-level decision " ++ "tree. Given labeled data, a new decision stump can be trained; or, an " ++ "existing decision stump can be used to classify points.", ++ // Long description. + "This program implements a decision stump, which is a single-level decision" + " tree. The decision stump will split on one dimension of the input data, " + "and will split into multiple buckets. The dimension and bins are selected" +@@ -61,7 +66,12 @@ PROGRAM_INFO("Decision Stump", + "\n\n" + "After training, a decision stump can be saved with the " + + PRINT_PARAM_STRING("output_model") + " output parameter. That stump may " +- "later be re-used in subsequent calls to this program (or others)."); ++ "later be re-used in subsequent calls to this program (or others).", ++ SEE_ALSO("Decision tree", "#decision_tree"), ++ SEE_ALSO("Decision stumps on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_stump"), ++ SEE_ALSO("mlpack::decision_stump::DecisionStump class documentation", ++ "@doxygen/classmlpack_1_1decision__stump_1_1DecisionStump.html")); + + // Datasets we might load. + PARAM_MATRIX_IN("training", "The dataset to train on.", "t"); +diff --git a/src/mlpack/methods/decision_tree/CMakeLists.txt b/src/mlpack/methods/decision_tree/CMakeLists.txt +index 448e4d62e..3f46f0d95 100644 +--- a/src/mlpack/methods/decision_tree/CMakeLists.txt ++++ b/src/mlpack/methods/decision_tree/CMakeLists.txt +@@ -27,3 +27,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_tree) + add_python_binding(decision_tree) ++add_markdown_docs(decision_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_tree/decision_tree_main.cpp b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +index 0e4be96c0..312b8e962 100644 +--- a/src/mlpack/methods/decision_tree/decision_tree_main.cpp ++++ b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +@@ -21,6 +21,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Decision tree", ++ // Short description. ++ "An implementation of an ID3-style decision tree for classification, which" ++ " supports categorical data. Given labeled data with numeric or " ++ "categorical features, a decision tree can be trained and saved; or, an " ++ "existing decision tree can be used for classification on new points.", ++ // Long description. + "Train and evaluate using a decision tree. Given a dataset containing " + "numeric or categorical features, and associated labels for each point in " + "the dataset, this program can train a decision tree on that data." +@@ -70,7 +76,15 @@ PROGRAM_INFO("Decision tree", + PRINT_DATASET("predictions") + ", one could call " + "\n\n" + + PRINT_CALL("decision_tree", "input_model", "tree", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("Decision stump", "#decision_stump"), ++ SEE_ALSO("Random forest", "#random_forest"), ++ SEE_ALSO("Decision trees on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_tree_learning"), ++ SEE_ALSO("Induction of Decision Trees (pdf)", ++ "https://link.springer.com/content/pdf/10.1007/BF00116251.pdf"), ++ SEE_ALSO("mlpack::tree::DecisionTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1DecisionTree.html")); + + // Datasets. + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", +diff --git a/src/mlpack/methods/det/CMakeLists.txt b/src/mlpack/methods/det/CMakeLists.txt +index 762c7b11c..58b402c7c 100644 +--- a/src/mlpack/methods/det/CMakeLists.txt ++++ b/src/mlpack/methods/det/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(det) + add_python_binding(det) ++add_markdown_docs(det "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp +index 135baa703..df2db8169 100644 +--- a/src/mlpack/methods/det/det_main.cpp ++++ b/src/mlpack/methods/det/det_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Density Estimation With Density Estimation Trees", ++ // Short description. ++ "An implementation of density estimation trees for the density estimation " ++ "task. Density estimation trees can be trained or used to predict the " ++ "density at locations given by query points.", ++ // Long description. + "This program performs a number of functions related to Density Estimation " + "Trees. The optimal Density Estimation Tree (DET) can be trained on a set " + "of data (specified by " + PRINT_PARAM_STRING("training") + ") using " +@@ -49,7 +54,15 @@ PROGRAM_INFO("Density Estimation With Density Estimation Trees", + "trained on the given training points, or a tree given as the parameter " + + PRINT_PARAM_STRING("input_model") + ". The density estimates for the test" + " points may be saved using the " + +- PRINT_PARAM_STRING("test_set_estimates") + " output parameter."); ++ PRINT_PARAM_STRING("test_set_estimates") + " output parameter.", ++ SEE_ALSO("Density estimation tree (DET) tutorial", ++ "@doxygen/dettutorial.html"), ++ SEE_ALSO("Density estimation on Wikipedia", ++ "https://en.wikipedia.org/wiki/Density_estimation"), ++ SEE_ALSO("Density estimation trees (pdf)", ++ "http://www.mlpack.org/papers/det.pdf"), ++ SEE_ALSO("mlpack::tree::DTree class documentation", ++ "@doxygen/classmlpack_1_1det_1_1DTree.html")); + + // Input data files. + PARAM_MATRIX_IN("training", "The data set on which to build a density " +diff --git a/src/mlpack/methods/emst/CMakeLists.txt b/src/mlpack/methods/emst/CMakeLists.txt +index 73cd431cb..7a073a8f9 100644 +--- a/src/mlpack/methods/emst/CMakeLists.txt ++++ b/src/mlpack/methods/emst/CMakeLists.txt +@@ -23,3 +23,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(emst) + add_python_binding(emst) ++add_markdown_docs(emst "cli;python" "geometry") +diff --git a/src/mlpack/methods/emst/emst_main.cpp b/src/mlpack/methods/emst/emst_main.cpp +index 3b0f9de98..e60ba7b64 100644 +--- a/src/mlpack/methods/emst/emst_main.cpp ++++ b/src/mlpack/methods/emst/emst_main.cpp +@@ -31,6 +31,10 @@ + #include "dtb.hpp" + + PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", ++ // Short description. ++ "An implementation of the Dual-Tree Boruvka algorithm for computing the " ++ "Euclidean minimum spanning tree of a set of input points.", ++ // Long description. + "This program can compute the Euclidean minimum spanning tree of a set of " + "input points using the dual-tree Boruvka algorithm." + "\n\n" +@@ -56,7 +60,14 @@ PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", + "The output matrix is a three-dimensional matrix, where each row indicates " + "an edge. The first dimension corresponds to the lesser index of the edge;" + " the second dimension corresponds to the greater index of the edge; and " +- "the third column corresponds to the distance between the two points."); ++ "the third column corresponds to the distance between the two points.", ++ SEE_ALSO("EMST Tutorial", "@doxygen/emst_tutorial.html"), ++ SEE_ALSO("Minimum spanning tree on Wikipedia", ++ "https://en.wikipedia.org/wiki/Minimum_spanning_tree"), ++ SEE_ALSO("Fast Euclidean Minimum Spanning Tree: Algorithm, Analysis, and " ++ "Applications (pdf)", "http://www.mlpack.org/papers/emst.pdf"), ++ SEE_ALSO("mlpack::emst::DualTreeBoruvka class documentation", ++ "@doxygen/classmlpack_1_1emst_1_1DualTreeBoruvka.html")); + + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); + PARAM_MATRIX_OUT("output", "Output data. Stored as an edge list.", "o"); +diff --git a/src/mlpack/methods/fastmks/CMakeLists.txt b/src/mlpack/methods/fastmks/CMakeLists.txt +index d9a6a6a8e..e6b6e55c1 100644 +--- a/src/mlpack/methods/fastmks/CMakeLists.txt ++++ b/src/mlpack/methods/fastmks/CMakeLists.txt +@@ -22,3 +22,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(fastmks) + add_python_binding(fastmks) ++add_markdown_docs(fastmks "cli;python" "geometry") +diff --git a/src/mlpack/methods/fastmks/fastmks_main.cpp b/src/mlpack/methods/fastmks/fastmks_main.cpp +index 1d8e7e7fb..fea273d65 100644 +--- a/src/mlpack/methods/fastmks/fastmks_main.cpp ++++ b/src/mlpack/methods/fastmks/fastmks_main.cpp +@@ -25,6 +25,12 @@ using namespace mlpack::metric; + using namespace mlpack::util; + + PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", ++ // Short description. ++ "An implementation of the single-tree and dual-tree fast max-kernel search" ++ " (FastMKS) algorithm. Given a set of reference points and a set of query" ++ " points, this can find the reference point with maximum kernel value for " ++ "each query point; trained models can be reused for future queries.", ++ // Long description. + "This program will find the k maximum kernels of a set of points, " + "using a query set and a reference set (which can optionally be the same " + "set). More specifically, for each point in the query set, the k points in" +@@ -51,7 +57,14 @@ PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", + "\n\n" + "This program performs FastMKS using a cover tree. The base used to build " + "the cover tree can be specified with the " + PRINT_PARAM_STRING("base") + +- " parameter."); ++ " parameter.", ++ SEE_ALSO("Fast max-kernel search tutorial (fastmks)", ++ "@doxygen/fmkstutorial.html"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Dual-tree Fast Exact Max-Kernel Search (pdf)", ++ "http://mlpack.org/papers/fmks.pdf"), ++ SEE_ALSO("mlpack::fastmks::FastMKS class documentation", ++ "@doxygen/classmlpack_1_1fastmks_1_1FastMKS.html")); + + // Model-building parameters. + PARAM_MATRIX_IN("reference", "The reference dataset.", "r"); +diff --git a/src/mlpack/methods/gmm/CMakeLists.txt b/src/mlpack/methods/gmm/CMakeLists.txt +index 74f9e8ace..964f490be 100644 +--- a/src/mlpack/methods/gmm/CMakeLists.txt ++++ b/src/mlpack/methods/gmm/CMakeLists.txt +@@ -23,7 +23,12 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(gmm_train) + add_python_binding(gmm_train) ++add_markdown_docs(gmm_train "cli;python" "clustering") ++ + add_cli_executable(gmm_generate) + add_python_binding(gmm_generate) ++add_markdown_docs(gmm_generate "cli;python" "clustering") ++ + add_cli_executable(gmm_probability) + add_python_binding(gmm_probability) ++add_markdown_docs(gmm_probability "cli;python" "clustering") +diff --git a/src/mlpack/methods/gmm/gmm_generate_main.cpp b/src/mlpack/methods/gmm/gmm_generate_main.cpp +index 784234941..4f3d10fdb 100644 +--- a/src/mlpack/methods/gmm/gmm_generate_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_generate_main.cpp +@@ -20,6 +20,10 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Sample Generator", ++ // Short description. ++ "A sample generator for pre-trained GMMs. Given a pre-trained GMM, this " ++ "can sample new points randomly from that distribution.", ++ // Long description. + "This program is able to generate samples from a pre-trained GMM (use " + "gmm_train to train a GMM). The pre-trained GMM must be specified with " + "the " + PRINT_PARAM_STRING("input_model") + " parameter. The number " +@@ -32,7 +36,13 @@ PROGRAM_INFO("GMM Sample Generator", + "samples in " + PRINT_DATASET("samples") + ":" + "\n\n" + + PRINT_CALL("gmm_generate", "input_model", "gmm", "samples", 100, "output", +- "samples")); ++ "samples"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM model to generate samples " + "from.", "m"); +diff --git a/src/mlpack/methods/gmm/gmm_probability_main.cpp b/src/mlpack/methods/gmm/gmm_probability_main.cpp +index f40c86628..6ba29585c 100644 +--- a/src/mlpack/methods/gmm/gmm_probability_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_probability_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Probability Calculator", ++ // Short description. ++ "A probability calculator for GMMs. Given a pre-trained GMM and a set of " ++ "points, this can compute the probability that each point is from the given" ++ " GMM.", ++ // Long description. + "This program calculates the probability that given points came from a " + "given GMM (that is, P(X | gmm)). The GMM is specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and the points are " +@@ -33,7 +38,13 @@ PROGRAM_INFO("GMM Probability Calculator", + PRINT_DATASET("probs") + ", the following command could be used:" + "\n\n" + + PRINT_CALL("gmm_probability", "input_model", "gmm", "input", "points", +- "output", "probs")); ++ "output", "probs"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM to use as model.", "m"); + PARAM_MATRIX_IN_REQ("input", "Input matrix to calculate probabilities of.", +diff --git a/src/mlpack/methods/gmm/gmm_train_main.cpp b/src/mlpack/methods/gmm/gmm_train_main.cpp +index c331d04dc..a01adfcb0 100644 +--- a/src/mlpack/methods/gmm/gmm_train_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_train_main.cpp +@@ -26,6 +26,11 @@ using namespace mlpack::kmeans; + using namespace std; + + PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", ++ // Short description. ++ "An implementation of the EM algorithm for training Gaussian mixture " ++ "models (GMMs). Given a dataset, this can train a GMM for future use " ++ "with other tools.", ++ // Long description. + "This program takes a parametric estimate of a Gaussian mixture model (GMM)" + " using the EM algorithm to find the maximum likelihood estimate. The " + "model may be saved and reused by other mlpack GMM tools." +@@ -84,7 +89,13 @@ PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", + ", the following command may be used: " + "\n\n" + + PRINT_CALL("gmm_train", "input_model", "gmm", "input", "data2", +- "gaussians", 6, "output_model", "new_gmm")); ++ "gaussians", 6, "output_model", "new_gmm"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + // Parameters for training. + PARAM_MATRIX_IN_REQ("input", "The training data on which the model will be " +diff --git a/src/mlpack/methods/hmm/CMakeLists.txt b/src/mlpack/methods/hmm/CMakeLists.txt +index 7add2baa3..7247430bb 100644 +--- a/src/mlpack/methods/hmm/CMakeLists.txt ++++ b/src/mlpack/methods/hmm/CMakeLists.txt +@@ -21,9 +21,16 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hmm_train) + add_python_binding(hmm_train) ++add_markdown_docs(hmm_train "cli;python" "misc. / other") ++ + add_cli_executable(hmm_loglik) + add_python_binding(hmm_loglik) ++add_markdown_docs(hmm_loglik "cli;python" "misc. / other") ++ + add_cli_executable(hmm_viterbi) + add_python_binding(hmm_viterbi) ++add_markdown_docs(hmm_viterbi "cli;python" "misc. / other") ++ + add_cli_executable(hmm_generate) + add_python_binding(hmm_generate) ++add_markdown_docs(hmm_generate "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/hmm/hmm_generate_main.cpp b/src/mlpack/methods/hmm/hmm_generate_main.cpp +index 0efc8dc84..cbfbe30bf 100644 +--- a/src/mlpack/methods/hmm/hmm_generate_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_generate_main.cpp +@@ -29,8 +29,13 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " +- "utility takes an already-trained HMM, specified as the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", ++ // Short description. ++ "A utility to generate random sequences from a pre-trained Hidden Markov " ++ "Model (HMM). The length of the desired sequence can be specified, and a " ++ "random sequence of observations is returned.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as the " + + PRINT_PARAM_STRING("model") + " parameter, and generates a random " + "observation sequence and hidden state sequence based on its parameters. " + "The observation sequence may be saved with the " + +@@ -47,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " + PRINT_DATASET("states") + ", the following command may be used: " + "\n\n" + + PRINT_CALL("hmm_generate", "model", "hmm", "length", 150, "output", +- "observations", "state", "states")); ++ "observations", "state", "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MODEL_IN_REQ(HMMModel, "model", "Trained HMM to generate sequences with.", + "m"); +diff --git a/src/mlpack/methods/hmm/hmm_loglik_main.cpp b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +index 6ebb3cbef..23a482603 100644 +--- a/src/mlpack/methods/hmm/hmm_loglik_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +@@ -26,8 +26,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " +- "utility takes an already-trained HMM, specified with the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", ++ // Short description. ++ "A utility for computing the log-likelihood of a sequence for Hidden Markov" ++ " Models (HMMs). Given a pre-trained HMM and an observation sequence, this" ++ " computes and returns the log-likelihood of that sequence being observed " ++ "from that HMM.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and evaluates the " + "log-likelihood of a sequence of observations, given with the " + + PRINT_PARAM_STRING("input") + " parameter. The computed log-likelihood is" +@@ -37,7 +43,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " + PRINT_DATASET("seq") + " with the pre-trained HMM " + PRINT_MODEL("hmm") + + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm")); ++ PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "File containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "File containing HMM.", "m"); +diff --git a/src/mlpack/methods/hmm/hmm_train_main.cpp b/src/mlpack/methods/hmm/hmm_train_main.cpp +index 109cfbddd..3e21085a1 100644 +--- a/src/mlpack/methods/hmm/hmm_train_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_train_main.cpp +@@ -27,9 +27,15 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " +- "Hidden Markov Model to be trained on labeled or unlabeled data. It " +- "support three types of HMMs: discrete HMMs, Gaussian HMMs, or GMM HMMs." ++PROGRAM_INFO("Hidden Markov Model (HMM) Training", ++ // Short description. ++ "An implementation of training algorithms for Hidden Markov Models (HMMs). " ++ "Given labeled or unlabeled data, an HMM can be trained for further use " ++ "with other mlpack HMM tools.", ++ // Long description. ++ "This program allows a Hidden Markov Model to be trained on labeled or " ++ "unlabeled data. It supports three types of HMMs: discrete HMMs, " ++ "Gaussian HMMs, or GMM HMMs." + "\n\n" + "Either one input sequence can be specified (with --input_file), or, a " + "file containing files in which input sequences can be found (when " +@@ -46,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " + "\n\n" + "Optionally, a pre-created HMM model can be used as a guess for the " + "transition matrix and emission probabilities; this is specifiable with " +- "--model_file."); ++ "--model_file.", ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_STRING_IN_REQ("input_file", "File containing input observations.", "i"); + PARAM_STRING_IN("type", "Type of HMM: discrete | gaussian | gmm.", "t", +diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +index d0e1168b3..1fa3c5f58 100644 +--- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +@@ -27,8 +27,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " +- "utility takes an already-trained HMM, specified as " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", ++ // Short description. ++ "A utility for computing the most probable hidden state sequence for Hidden" ++ " Markov Models (HMMs). Given a pre-trained HMM and an observed sequence, " ++ "this uses the Viterbi algorithm to compute and return the most probable " ++ "hidden state sequence.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as " + + PRINT_PARAM_STRING("input_model") + ", and evaluates the most probable " + "hidden state sequence of a given sequence of observations (specified as " + "'" + PRINT_PARAM_STRING("input") + ", using the Viterbi algorithm. The " +@@ -41,7 +47,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " + ", the following command could be used:" + "\n\n" + + PRINT_CALL("hmm_viterbi", "input", "obs", "input_model", "hmm", "output", +- "states")); ++ "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "Matrix containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "Trained HMM to use.", "m"); +diff --git a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +index b573a19c6..2a94415b3 100644 +--- a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt ++++ b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +@@ -30,3 +30,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hoeffding_tree) + add_python_binding(hoeffding_tree) ++add_markdown_docs(hoeffding_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +index 46437b4ad..45b58713c 100644 +--- a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp ++++ b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +@@ -26,6 +26,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Hoeffding trees", ++ // Short description. ++ "An implementation of Hoeffding trees, a form of streaming decision tree " ++ "for classification. Given labeled data, a Hoeffding tree can be trained " ++ "and saved for later use, or a pre-trained Hoeffding tree can be used for " ++ "predicting the classifications of new points.", ++ // Long description. + "This program implements Hoeffding trees, a form of streaming decision tree" + " suited best for large (or streaming) datasets. This program supports " + "both categorical and numeric data. Given an input dataset, this program " +@@ -70,7 +76,13 @@ PROGRAM_INFO("Hoeffding trees", + PRINT_DATASET("class_probs") + " with the following command: " + "\n\n" + + PRINT_CALL("hoeffding_tree", "input_model", "tree", "test", "test_set", +- "predictions", "predictions", "probabilities", "class_probs")); ++ "predictions", "predictions", "probabilities", "class_probs"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Mining High-Speed Data Streams (pdf)", ++ "http://dm.cs.washington.edu/papers/vfdt-kdd00.pdf"), ++ SEE_ALSO("mlpack::tree::HoeffdingTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1HoeffdingTree.html")); + + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", + "t"); +diff --git a/src/mlpack/methods/kernel_pca/CMakeLists.txt b/src/mlpack/methods/kernel_pca/CMakeLists.txt +index c14a9f3ec..9bd0cd3a0 100644 +--- a/src/mlpack/methods/kernel_pca/CMakeLists.txt ++++ b/src/mlpack/methods/kernel_pca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kernel_pca) + add_python_binding(kernel_pca) ++add_markdown_docs(kernel_pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +index 7ca626812..af6c5c17f 100644 +--- a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp ++++ b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +@@ -41,6 +41,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Kernel Principal Components Analysis", ++ // Short description. ++ "An implementation of Kernel Principal Components Analysis (KPCA). This " ++ "can be used to perform nonlinear dimensionality reduction or preprocessing" ++ " on a given dataset.", ++ // Long description. + "This program performs Kernel Principal Components Analysis (KPCA) on the " + "specified dataset with the specified kernel. This will transform the " + "data onto the kernel principal components, and optionally reduce the " +@@ -93,7 +98,13 @@ PROGRAM_INFO("Kernel Principal Components Analysis", + "the kernel matrix; to specify the sampling scheme, the " + + PRINT_PARAM_STRING("sampling") + " parameter is used. The " + "sampling scheme for the Nystr\u00F6m method can be chosen from the " +- "following list: 'kmeans', 'random', 'ordered'."); ++ "following list: 'kmeans', 'random', 'ordered'.", ++ SEE_ALSO("Kernel principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Kernel_principal_component_analysis"), ++ SEE_ALSO("Kernel Principal Component Analysis (pdf)", ++ "http://pca.narod.ru/scholkopf_kernel.pdf"), ++ SEE_ALSO("mlpack::kpca::KernelPCA class documentation", ++ "@doxygen/classmlpack_1_1kpca_1_1KernelPCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform KPCA on.", "i"); + PARAM_MATRIX_OUT("output", "Matrix to save modified dataset to.", "o"); +@@ -107,9 +118,9 @@ PARAM_INT_IN("new_dimensionality", "If not 0, reduce the dimensionality of " + PARAM_FLAG("center", "If set, the transformed data will be centered about the " + "origin.", "c"); + +-PARAM_FLAG("nystroem_method", "If set, the nystroem method will be used.", "n"); ++PARAM_FLAG("nystroem_method", "If set, the Nystroem method will be used.", "n"); + +-PARAM_STRING_IN("sampling", "Sampling scheme to use for the nystroem method: " ++PARAM_STRING_IN("sampling", "Sampling scheme to use for the Nystroem method: " + "'kmeans', 'random', 'ordered'", "s", "kmeans"); + + PARAM_DOUBLE_IN("kernel_scale", "Scale, for 'hyptan' kernel.", "S", 1.0); +diff --git a/src/mlpack/methods/kmeans/CMakeLists.txt b/src/mlpack/methods/kmeans/CMakeLists.txt +index bf4990083..9cfec0fdc 100644 +--- a/src/mlpack/methods/kmeans/CMakeLists.txt ++++ b/src/mlpack/methods/kmeans/CMakeLists.txt +@@ -40,3 +40,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kmeans) + add_python_binding(kmeans) ++add_markdown_docs(kmeans "cli;python" "clustering") +diff --git a/src/mlpack/methods/kmeans/kmeans_main.cpp b/src/mlpack/methods/kmeans/kmeans_main.cpp +index b0319ccb5..9d3f40c7d 100644 +--- a/src/mlpack/methods/kmeans/kmeans_main.cpp ++++ b/src/mlpack/methods/kmeans/kmeans_main.cpp +@@ -28,11 +28,17 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " +- "on the given dataset. It can return the learned cluster assignments, and " +- "the centroids of the clusters. Empty clusters are not allowed by default;" +- " when a cluster becomes empty, the point furthest from the centroid of the" +- " cluster with maximum variance is taken to fill that cluster." ++PROGRAM_INFO("K-Means Clustering", ++ // Short description. ++ "An implementation of several strategies for efficient k-means clustering. " ++ "Given a dataset and a value of k, this computes and returns a k-means " ++ "clustering on that data.", ++ // Long description. ++ "This program performs K-Means clustering on the given dataset. It can " ++ "return the learned cluster assignments, and the centroids of the clusters." ++ " Empty clusters are not allowed by default; when a cluster becomes empty," ++ " the point furthest from the centroid of the cluster with maximum variance" ++ " is taken to fill that cluster." + "\n\n" + "Optionally, the Bradley and Fayyad approach (\"Refining initial points for" + " k-means clustering\", 1998) can be used to select initial points by " +@@ -85,7 +91,21 @@ PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " + "following command may be used:" + "\n\n" + + PRINT_CALL("kmeans", "input", "data", "initial_centroids", "initial", +- "clusters", 10, "max_iterations", 500, "centroid", "final")); ++ "clusters", 10, "max_iterations", 500, "centroid", "final"), ++ SEE_ALSO("K-Means tutorial", "@doxygen/kmtutorial.html"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Using the triangle inequality to accelerate k-means (pdf)", ++ "http://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf"), ++ SEE_ALSO("Making k-means even faster (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.586.2554" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("Accelerating exact k-means algorithms with geometric reasoning " ++ "(pdf)", "http://reports-archive.adm.cs.cmu.edu/anon/anon/usr/ftp/" ++ "usr0/ftp/2000/CMU-CS-00-105.pdf"), ++ SEE_ALSO("A dual-tree algorithm for fast k-means clustering with large k " ++ "(pdf)", "http://www.ratml.org/pub/pdf/2017dual.pdf"), ++ SEE_ALSO("mlpack::kmeans::KMeans class documentation", ++ "@doxygen/classmlpack_1_1kmeans_1_1KMeans.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/lars/CMakeLists.txt b/src/mlpack/methods/lars/CMakeLists.txt +index 1388f24be..7e4cb7890 100644 +--- a/src/mlpack/methods/lars/CMakeLists.txt ++++ b/src/mlpack/methods/lars/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(lars) + add_python_binding(lars) ++add_markdown_docs(lars "cli;python" "regression") +diff --git a/src/mlpack/methods/lars/lars_main.cpp b/src/mlpack/methods/lars/lars_main.cpp +index 040c5f0c3..e0b2f38f3 100644 +--- a/src/mlpack/methods/lars/lars_main.cpp ++++ b/src/mlpack/methods/lars/lars_main.cpp +@@ -21,10 +21,16 @@ using namespace mlpack; + using namespace mlpack::regression; + using namespace mlpack::util; + +-PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " +- "(Stagewise/laSso). This is a stage-wise homotopy-based algorithm for " +- "L1-regularized linear regression (LASSO) and L1+L2-regularized linear " +- "regression (Elastic Net)." ++PROGRAM_INFO("LARS", ++ // Short description. ++ "An implementation of Least Angle Regression (Stagewise/laSso), also known" ++ " as LARS. This can train a LARS/LASSO/Elastic Net model and use that " ++ "model or a pre-trained model to output regression predictions for a test " ++ "set.", ++ // Long description. ++ "An implementation of LARS: Least Angle Regression (Stagewise/laSso). " ++ "This is a stage-wise homotopy-based algorithm for L1-regularized linear " ++ "regression (LASSO) and L1+L2-regularized linear regression (Elastic Net)." + "\n\n" + "This program is able to train a LARS/LASSO/Elastic Net model or load a " + "model from file, output regression predictions for a test set, and save " +@@ -80,7 +86,12 @@ PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " + "and save those responses to " + PRINT_DATASET("test_predictions") + ": " + "\n\n" + + PRINT_CALL("lars", "input_model", "lasso_model", "test", "test", +- "output_predictions", "test_predictions")); ++ "output_predictions", "test_predictions"), ++ SEE_ALSO("@linear_regression", "#linear_regression"), ++ SEE_ALSO("Least angle regression (pdf)", ++ "http://mlpack.org/papers/lars.pdf"), ++ SEE_ALSO("mlpack::regression::LARS C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LARS.html")); + + PARAM_TMATRIX_IN("input", "Matrix of covariates (X).", "i"); + PARAM_MATRIX_IN("responses", "Matrix of responses/observations (y).", "r"); +diff --git a/src/mlpack/methods/linear_regression/CMakeLists.txt b/src/mlpack/methods/linear_regression/CMakeLists.txt +index 58a7426e8..b4e63cdca 100644 +--- a/src/mlpack/methods/linear_regression/CMakeLists.txt ++++ b/src/mlpack/methods/linear_regression/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(linear_regression) + add_python_binding(linear_regression) ++add_markdown_docs(linear_regression "cli;python" "regression") +diff --git a/src/mlpack/methods/linear_regression/linear_regression_main.cpp b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +index c1e33cd06..1cb58a85c 100644 +--- a/src/mlpack/methods/linear_regression/linear_regression_main.cpp ++++ b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +@@ -22,6 +22,12 @@ using namespace arma; + using namespace std; + + PROGRAM_INFO("Simple Linear Regression and Prediction", ++ // Short description. ++ "An implementation of simple linear regression and ridge regression using " ++ "ordinary least squares. Given a dataset and responses, a model can be " ++ "trained and saved for later use, or a pre-trained model can be used to " ++ "output regression predictions for a test set.", ++ // Long description. + "An implementation of simple linear regression and simple ridge regression " + "using ordinary least squares. This solves the problem" + "\n\n" +@@ -63,7 +69,13 @@ PROGRAM_INFO("Simple Linear Regression and Prediction", + "used:" + "\n\n" + + PRINT_CALL("linear_regression", "input_model", "lr_model", "test", "X_test", +- "output_predictions", "X_test_responses")); ++ "output_predictions", "X_test_responses"), ++ SEE_ALSO("Linear/ridge regression tutorial", "@doxygen/lrtutorial.html"), ++ SEE_ALSO("@lars", "#lars"), ++ SEE_ALSO("Linear regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Linear_regression"), ++ SEE_ALSO("mlpack::regression::LinearRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LinearRegression.html")); + + PARAM_MATRIX_IN("training", "Matrix containing training set X (regressors).", + "t"); +diff --git a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +index 6720525c7..d4b498e55 100644 +--- a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt ++++ b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(local_coordinate_coding) + add_python_binding(local_coordinate_coding) ++add_markdown_docs(local_coordinate_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +index f0ee7e6dc..16d8e13d8 100644 +--- a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp ++++ b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +@@ -24,6 +24,12 @@ using namespace mlpack::sparse_coding; // For NothingInitializer. + using namespace mlpack::util; + + PROGRAM_INFO("Local Coordinate Coding", ++ // Short description. ++ "An implementation of Local Coordinate Coding (LCC), a data transformation " ++ "technique. Given input data, this transforms each point to be expressed " ++ "as a linear combination of a few points in the dataset; once an LCC model " ++ "is trained, it can be used to transform points later also.", ++ // Long description. + "An implementation of Local Coordinate Coding (LCC), which " + "codes data that approximately lives on a manifold using a variation of l1-" + "norm regularized sparse coding. Given a dense data matrix X with n points" +@@ -67,7 +73,13 @@ PROGRAM_INFO("Local Coordinate Coding", + "be used:" + "\n\n" + + PRINT_CALL("local_coordinate_coding", "input_model", "lcc_model", "test", +- "points", "codes", "new_codes")); ++ "points", "codes", "new_codes"), ++ SEE_ALSO("@sparse_coding", "#sparse_coding"), ++ SEE_ALSO("Nonlinear learning using local coordinate coding (pdf)", ++ "https://papers.nips.cc/paper/3875-nonlinear-learning-using-local-" ++ "coordinate-coding.pdf"), ++ SEE_ALSO("mlpack::lcc::LocalCoordinateCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1lcc_1_1LocalCoordinateCoding.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +diff --git a/src/mlpack/methods/logistic_regression/CMakeLists.txt b/src/mlpack/methods/logistic_regression/CMakeLists.txt +index d7edd6ba1..673e214c6 100644 +--- a/src/mlpack/methods/logistic_regression/CMakeLists.txt ++++ b/src/mlpack/methods/logistic_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(logistic_regression) + add_python_binding(logistic_regression) ++add_markdown_docs(logistic_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +index 2a0ed23a7..385525ab0 100644 +--- a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp ++++ b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +@@ -24,6 +24,11 @@ using namespace mlpack::optimization; + using namespace mlpack::util; + + PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", ++ // Short description. ++ "An implementation of L2-regularized logistic regression for two-class " ++ "classification. Given labeled data, a model can be trained and saved for " ++ "future use; or, a pre-trained model can be used to classify new points.", ++ // Long description. + "An implementation of L2-regularized logistic regression using either the " + "L-BFGS optimizer or SGD (stochastic gradient descent). This solves the " + "regression problem" +@@ -39,8 +44,8 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + "those things at once. In addition, this program allows classification on " + "a test dataset (specified with the " + PRINT_PARAM_STRING("test") + " " + "parameter) and the classification results may be saved with the " + +- PRINT_PARAM_STRING("output") + " output parameter. The trained logistic " +- "regression model may be saved using the " + ++ PRINT_PARAM_STRING("output") + " output parameter." ++ " The trained logistic regression model may be saved using the " + + PRINT_PARAM_STRING("output_model") + " output parameter." + "\n\n" + "The training data, if specified, may have class labels as its last " +@@ -95,7 +100,13 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + PRINT_DATASET("predictions") + "', the following command may be used: " + "\n\n" + + PRINT_CALL("logistic_regression", "input_model", "lr_model", "test", "test", +- "output", "predictions")); ++ "output", "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Logistic regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Logistic_regression"), ++ SEE_ALSO("mlpack::regression::LogisticRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LogisticRegression.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/lsh/CMakeLists.txt b/src/mlpack/methods/lsh/CMakeLists.txt +index 758a2152b..4ec7baacf 100644 +--- a/src/mlpack/methods/lsh/CMakeLists.txt ++++ b/src/mlpack/methods/lsh/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # sets with p-stable LSH. + add_cli_executable(lsh) + add_python_binding(lsh) ++add_markdown_docs(lsh "cli;python" "geometry") +diff --git a/src/mlpack/methods/lsh/lsh_main.cpp b/src/mlpack/methods/lsh/lsh_main.cpp +index d9923e02b..c082e10aa 100644 +--- a/src/mlpack/methods/lsh/lsh_main.cpp ++++ b/src/mlpack/methods/lsh/lsh_main.cpp +@@ -25,6 +25,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", ++ // Short description. ++ "An implementation of approximate k-nearest-neighbor search with " ++ "locality-sensitive hashing (LSH). Given a set of reference points and a " ++ "set of query points, this will compute the k approximate nearest neighbors" ++ " of each query point in the reference set; models can be saved for future " ++ "use.", ++ // Long description. + "This program will calculate the k approximate-nearest-neighbors of a set " + "of points using locality-sensitive hashing. You may specify a separate set" + " of reference points and query points, or just a reference set which will " +@@ -49,7 +56,15 @@ PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", + " parameter can be specified to set the random seed." + "\n\n" + "This program also has many other parameters to control its functionality;" +- " see the parameter-specific documentation for more information."); ++ " see the parameter-specific documentation for more information.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("Locality-sensitive hashing on Wikipedia", ++ "https://en.wikipedia.org/wiki/Locality-sensitive_hashing"), ++ SEE_ALSO("Locality-sensitive hashing scheme based on p-stable distributions" ++ " (pdf)", "http://mlpack.org/papers/lsh.pdf"), ++ SEE_ALSO("mlpack::neighbor::LSHSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1LSHSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/mean_shift/CMakeLists.txt b/src/mlpack/methods/mean_shift/CMakeLists.txt +index 1087ab532..5aced5225 100644 +--- a/src/mlpack/methods/mean_shift/CMakeLists.txt ++++ b/src/mlpack/methods/mean_shift/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(mean_shift) + add_python_binding(mean_shift) ++add_markdown_docs(mean_shift "cli;python" "clustering") +diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +index 4710ccaad..ac7f17e18 100644 +--- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp ++++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +@@ -23,9 +23,15 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " +- "clustering on the given dataset, storing the learned cluster assignments " +- "either as a column of labels in the input dataset or separately." ++PROGRAM_INFO("Mean Shift Clustering", ++ // Short description. ++ "A fast implementation of mean-shift clustering using dual-tree range " ++ "search. Given a dataset, this uses the mean shift algorithm to produce " ++ "and return a clustering of the data.", ++ // Long description. ++ "This program performs mean shift clustering on the given dataset, storing " ++ "the learned cluster assignments either as a column of labels in the input " ++ "dataset or separately." + "\n\n" + "The input dataset should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter, and the radius used for search" +@@ -42,7 +48,16 @@ PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " + PRINT_DATASET("data") + " and store the centroids to " + + PRINT_DATASET("centroids") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids")); ++ PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids"), ++ SEE_ALSO("@kmeans", "#kmeans"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Mean shift on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mean_shift"), ++ SEE_ALSO("Mean Shift, Mode Seeking, and Clustering (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.510.1222" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::mean_shift::MeanShift C++ class documentation", ++ "@doxygen/classmlpack_1_1meanshift_1_1MeanShift.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/naive_bayes/CMakeLists.txt b/src/mlpack/methods/naive_bayes/CMakeLists.txt +index f15dc229c..b2afe0707 100644 +--- a/src/mlpack/methods/naive_bayes/CMakeLists.txt ++++ b/src/mlpack/methods/naive_bayes/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nbc) + add_python_binding(nbc) ++add_markdown_docs(nbc "cli;python" "classification") +diff --git a/src/mlpack/methods/naive_bayes/nbc_main.cpp b/src/mlpack/methods/naive_bayes/nbc_main.cpp +index 68a4a70e0..612d675d0 100644 +--- a/src/mlpack/methods/naive_bayes/nbc_main.cpp ++++ b/src/mlpack/methods/naive_bayes/nbc_main.cpp +@@ -26,6 +26,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Parametric Naive Bayes Classifier", ++ // Short description. ++ "An implementation of the Naive Bayes Classifier, used for classification. " ++ "Given labeled data, an NBC model can be trained and saved, or, a " ++ "pre-trained model can be used for classification.", ++ // Long description. + "This program trains the Naive Bayes classifier on the given labeled " + "training set, or loads a model from the given model file, and then may use" + " that trained model to classify the points in a given test set." +@@ -66,7 +71,14 @@ PROGRAM_INFO("Parametric Naive Bayes Classifier", + "may be used:" + "\n\n" + + PRINT_CALL("nbc", "input_model", "nbc_model", "test", "test_set", "output", +- "predictions")); ++ "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Naive Bayes classifier on Wikipedia", ++ "https://en.wikipedia.org/wiki/Naive_Bayes_classifier"), ++ SEE_ALSO("mlpack::naive_bayes::NaiveBayesClassifier C++ class " ++ "documentation", "@doxygen/classmlpack_1_1naive__bayes_1_1" ++ "NaiveBayesClassifier.html")); + + // A struct for saving the model with mappings. + struct NBCModel +diff --git a/src/mlpack/methods/nca/CMakeLists.txt b/src/mlpack/methods/nca/CMakeLists.txt +index 777eef39b..7b1fd98f5 100644 +--- a/src/mlpack/methods/nca/CMakeLists.txt ++++ b/src/mlpack/methods/nca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nca) + add_python_binding(nca) ++add_markdown_docs(nca "cli;python" "transformations") +diff --git a/src/mlpack/methods/nca/nca_main.cpp b/src/mlpack/methods/nca/nca_main.cpp +index 5e57e38af..47b307469 100644 +--- a/src/mlpack/methods/nca/nca_main.cpp ++++ b/src/mlpack/methods/nca/nca_main.cpp +@@ -22,6 +22,12 @@ + + // Define parameters. + PROGRAM_INFO("Neighborhood Components Analysis (NCA)", ++ // Short description. ++ "An implementation of neighborhood components analysis, a distance learning" ++ " technique that can be used for preprocessing. Given a labeled dataset, " ++ "this uses NCA, which seeks to improve the k-nearest-neighbor " ++ "classification, and returns the learned distance metric.", ++ // Long description. + "This program implements Neighborhood Components Analysis, both a linear " + "dimensionality reduction technique and a distance learning technique. The" + " method seeks to improve k-nearest-neighbor classification on a dataset " +@@ -82,7 +88,14 @@ PROGRAM_INFO("Neighborhood Components Analysis (NCA)", + "mlpack L-BFGS documentation (in lbfgs.hpp) or the vast set of published " + "literature on L-BFGS." + "\n\n" +- "By default, the SGD optimizer is used."); ++ "By default, the SGD optimizer is used.", ++ SEE_ALSO("Neighbourhood components analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Neighbourhood_components_analysis"), ++ SEE_ALSO("Neighbourhood components analysis (pdf)", ++ "http://papers.nips.cc/paper/2566-neighbourhood-components-" ++ "analysis.pdf"), ++ SEE_ALSO("mlpack::nca::NCA C++ class documentation", ++ "@doxygen/classmlpack_1_1nca_1_1NCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to run NCA on.", "i"); + PARAM_MATRIX_OUT("output", "Output matrix for learned distance matrix.", "o"); +diff --git a/src/mlpack/methods/neighbor_search/CMakeLists.txt b/src/mlpack/methods/neighbor_search/CMakeLists.txt +index 1958ebe17..789beb295 100644 +--- a/src/mlpack/methods/neighbor_search/CMakeLists.txt ++++ b/src/mlpack/methods/neighbor_search/CMakeLists.txt +@@ -29,5 +29,8 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # Add mlpack_knn and mlpack_kfn executables. + add_cli_executable(knn) + add_python_binding(knn) ++add_markdown_docs(knn "cli;python" "geometry") ++ + add_cli_executable(kfn) + add_python_binding(kfn) ++add_markdown_docs(kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp +index 56415c3ae..57e2ffdf6 100644 +--- a/src/mlpack/methods/neighbor_search/kfn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp +@@ -34,6 +34,12 @@ typedef NSModel KFNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Furthest-Neighbors Search", ++ // Short description. ++ "An implementation of k-furthest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k furthest neighbors in the reference set of each query" ++ " point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-furthest-neighbors of a set of " + "points. You may specify a separate set of reference points and query " + "points, or just a reference set which will be used as both the reference " +@@ -51,7 +57,13 @@ PROGRAM_INFO("k-Furthest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th furthest neighbor from the point in the " + "query set with index i. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@approx_kfn", "#approx_kfn"), ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp +index 336315dea..46a2b53bf 100644 +--- a/src/mlpack/methods/neighbor_search/knn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/knn_main.cpp +@@ -36,6 +36,12 @@ typedef NSModel KNNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Nearest-Neighbors Search", ++ // Short description. ++ "An implementation of k-nearest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k nearest neighbors in the reference set of each query " ++ "point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-nearest-neighbors of a set of " + "points using kd-trees or cover trees (cover tree support is experimental " + "and may be slow). You may specify a separate set of " +@@ -53,7 +59,16 @@ PROGRAM_INFO("k-Nearest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th nearest neighbor from the point in the " + "query set with index i. Row j and column i in the distances output matrix" +- " corresponds to the distance between those two points."); ++ " corresponds to the distance between those two points.", ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("@kfn", "#kfn"), ++ SEE_ALSO("NeighborSearch tutorial (k-nearest-neighbors)", ++ "@doxygen/nstutorial.html"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/nmf/CMakeLists.txt b/src/mlpack/methods/nmf/CMakeLists.txt +index 18e01f1b4..f1c4666f3 100644 +--- a/src/mlpack/methods/nmf/CMakeLists.txt ++++ b/src/mlpack/methods/nmf/CMakeLists.txt +@@ -1,2 +1,3 @@ + add_cli_executable(nmf) + add_python_binding(nmf) ++add_markdown_docs(nmf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/nmf/nmf_main.cpp b/src/mlpack/methods/nmf/nmf_main.cpp +index e39273eb2..1e9e4a539 100644 +--- a/src/mlpack/methods/nmf/nmf_main.cpp ++++ b/src/mlpack/methods/nmf/nmf_main.cpp +@@ -28,10 +28,15 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " +- "non-negative matrix factorization on the given dataset, storing the " +- "resulting decomposed matrices in the specified files. For an input " +- "dataset V, NMF decomposes V into two matrices W and H such that " ++PROGRAM_INFO("Non-negative Matrix Factorization", ++ // Short description. ++ "An implementation of non-negative matrix factorization. This can be used " ++ "to decompose an input dataset into two low-rank non-negative components.", ++ // Long description. ++ "This program performs non-negative matrix factorization on the given " ++ "dataset, storing the resulting decomposed matrices in the specified " ++ "files. For an input dataset V, NMF decomposes V into two matrices W " ++ "and H such that " + "\n\n" + "V = W * H" + "\n\n" +@@ -60,7 +65,17 @@ PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " + PRINT_DATASET("H") + ", the following command could be used: " + "\n\n" + + PRINT_CALL("nmf", "input", "V", "w", "W", "h", "H", "rank", 10, +- "update_rules", "multdist")); ++ "update_rules", "multdist"), ++ SEE_ALSO("@cf", "#cf"), ++ SEE_ALSO("Alternating matrix factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Non-negative matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Non-negative_matrix_factorization"), ++ SEE_ALSO("Algorithms for non-negative matrix factorization (pdf)", ++ "http://papers.nips.cc/paper/1861-algorithms-for-non-negative-matrix-" ++ "factorization.pdf"), ++ SEE_ALSO("mlpack::amf::AMF C++ class documentation", ++ "@doxygen/classmlpack_1_1amf_1_1AMF.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform NMF on.", "i"); +diff --git a/src/mlpack/methods/pca/CMakeLists.txt b/src/mlpack/methods/pca/CMakeLists.txt +index 097598ee8..8b317c4fc 100644 +--- a/src/mlpack/methods/pca/CMakeLists.txt ++++ b/src/mlpack/methods/pca/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(pca) + add_python_binding(pca) ++add_markdown_docs(pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/pca/pca_main.cpp b/src/mlpack/methods/pca/pca_main.cpp +index 4176f0680..493a80026 100644 +--- a/src/mlpack/methods/pca/pca_main.cpp ++++ b/src/mlpack/methods/pca/pca_main.cpp +@@ -26,12 +26,18 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Principal Components Analysis", "This program performs principal " +- "components analysis on the given dataset using the exact, randomized, " +- "randomized block Krylov, or QUIC SVD method. It will transform the data " +- "onto its principal components, optionally performing dimensionality " +- "reduction by ignoring the principal components with the smallest " +- "eigenvalues." ++PROGRAM_INFO("Principal Components Analysis", ++ // Short description. ++ "An implementation of several strategies for principal components analysis " ++ "(PCA), a common preprocessing step. Given a dataset and a desired new " ++ "dimensionality, this can reduce the dimensionality of the data using the " ++ "linear transformation determined by PCA.", ++ // Long description. ++ "This program performs principal components analysis on the given dataset " ++ "using the exact, randomized, randomized block Krylov, or QUIC SVD method. " ++ "It will transform the data onto its principal components, optionally " ++ "performing dimensionality reduction by ignoring the principal components " ++ "with the smallest eigenvalues." + "\n\n" + "Use the " + PRINT_PARAM_STRING("input") + " parameter to specify the " + "dataset to perform PCA on. A desired new dimensionality can be specified " +@@ -52,7 +58,11 @@ PROGRAM_INFO("Principal Components Analysis", "This program performs principal " + PRINT_DATASET("data_mod") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("pca", "input", "data", "new_dimensionality", 5, +- "decomposition_method", "randomized", "output", "data_mod")); ++ "decomposition_method", "randomized", "output", "data_mod"), ++ SEE_ALSO("Principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Principal_component_analysis"), ++ SEE_ALSO("mlpack::pca::PCA C++ class documentation", ++ "@doxygen/classmlpack_1_1pca_1_1PCA.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform PCA on.", "i"); +diff --git a/src/mlpack/methods/perceptron/CMakeLists.txt b/src/mlpack/methods/perceptron/CMakeLists.txt +index f0b4afc59..96a003562 100644 +--- a/src/mlpack/methods/perceptron/CMakeLists.txt ++++ b/src/mlpack/methods/perceptron/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(perceptron) + add_python_binding(perceptron) ++add_markdown_docs(perceptron "cli;python" "classification") +diff --git a/src/mlpack/methods/perceptron/perceptron_main.cpp b/src/mlpack/methods/perceptron/perceptron_main.cpp +index d23e6694b..5dde3939f 100644 +--- a/src/mlpack/methods/perceptron/perceptron_main.cpp ++++ b/src/mlpack/methods/perceptron/perceptron_main.cpp +@@ -26,6 +26,12 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Perceptron", ++ // Short description. ++ "An implementation of a perceptron---a single level neural network--=for " ++ "classification. Given labeled data, a perceptron can be trained and saved" ++ " for future use; or, a pre-trained perceptron can be used for " ++ "classification on new points.", ++ // Long description. + "This program implements a perceptron, which is a single level neural " + "network. The perceptron makes its predictions based on a linear predictor " + "function combining a set of weights with the feature vector. The " +@@ -75,7 +81,12 @@ PROGRAM_INFO("Perceptron", + "cannot pass a perceptron model trained on 2 classes and then re-train with" + " a 4-class dataset. Similarly, attempting classification on a " + "3-dimensional dataset with a perceptron that has been trained on 8 " +- "dimensions will cause an error."); ++ "dimensions will cause an error.", ++ SEE_ALSO("@adaboost", "#adaboost"), ++ SEE_ALSO("Perceptron on Wikipedia", ++ "https://en.wikipedia.org/wiki/Perceptron"), ++ SEE_ALSO("mlpack::perceptron::Perceptron C++ class documentation", ++ "@doxygen/classmlpack_1_1perceptron_1_1Perceptron.html")); + + // When we save a model, we must also save the class mappings. So we use this + // auxiliary structure to store both the perceptron and the mapping, and we'll +diff --git a/src/mlpack/methods/preprocess/CMakeLists.txt b/src/mlpack/methods/preprocess/CMakeLists.txt +index af9299c64..a65487981 100644 +--- a/src/mlpack/methods/preprocess/CMakeLists.txt ++++ b/src/mlpack/methods/preprocess/CMakeLists.txt +@@ -15,10 +15,17 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + #add_cli_executable(preprocess_stats) + add_cli_executable(preprocess_split) + add_python_binding(preprocess_split) ++add_markdown_docs(preprocess_split "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_binarize) + add_python_binding(preprocess_binarize) ++add_markdown_docs(preprocess_binarize "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_describe) + add_python_binding(preprocess_describe) ++add_markdown_docs(preprocess_describe "cli;python" "preprocessing") ++ + #add_cli_executable(preprocess_scan) + add_cli_executable(preprocess_imputer) + #add_python_binding(preprocess_imputer) ++add_markdown_docs(preprocess_imputer "cli" "preprocessing") +diff --git a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +index 53e6fb056..9d69834af 100644 +--- a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +@@ -14,7 +14,13 @@ + #include + #include + +-PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " ++PROGRAM_INFO("Binarize Data", ++ // Short description. ++ "A utility to binarize a dataset. Given a dataset, this utility converts " ++ "each value in the desired dimension(s) to 0 or 1; this can be a useful " ++ "preprocessing step.", ++ // Long description. ++ "This utility takes a dataset and binarizes the " + "variables into either 0 or 1 given threshold. User can apply binarization " + "on a dimension or the whole dataset. The dimension to apply binarization " + "to can be specified using the " + PRINT_PARAM_STRING("dimension") + +@@ -38,7 +44,10 @@ PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " + PRINT_DATASET("X") + ", we could instead run" + "\n\n" + + PRINT_CALL("preprocess_binarize", "input", "X", "threshold", 5.0, +- "dimension", 0, "output", "Y")); ++ "dimension", 0, "output", "Y"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +index 744f826a5..43b6df7f5 100644 +--- a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +@@ -22,12 +22,17 @@ using namespace mlpack::util; + using namespace std; + using namespace boost; + +-PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " +- "prints out the descriptive statistics of the data. Descriptive statistics " +- "is the discipline of quantitatively describing the main features of a " +- "collection of information, or the quantitative description itself. The " +- "program does not modify the original file, but instead prints out the " +- "statistics to the console. The printed result will look like a table." ++PROGRAM_INFO("Descriptive Statistics", ++ // Short description. ++ "A utility for printing descriptive statistics about a dataset. This " ++ "prints a number of details about a dataset in a tabular format.", ++ // Long description. ++ "This utility takes a dataset and prints out the descriptive statistics " ++ "of the data. Descriptive statistics is the discipline of quantitatively " ++ "describing the main features of a collection of information, or the " ++ "quantitative description itself. The program does not modify the original " ++ "file, but instead prints out the statistics to the console. The printed " ++ "result will look like a table." + "\n\n" + "Optionally, width and precision of the output can be adjusted by a user " + "using the " + PRINT_PARAM_STRING("width") + " and " + +@@ -47,7 +52,10 @@ PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " + "the dataset as a population, we could run" + "\n\n" + + PRINT_CALL("preprocess_describe", "input", "X", "width", 10, "precision", 5, +- "verbose", true)); ++ "verbose", true), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data,", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +index 848bbe9cf..1825334ad 100644 +--- a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +@@ -23,8 +23,14 @@ + #include + #include + +-PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " +- "defined missing variable to another to provide more meaningful analysis " ++PROGRAM_INFO("Impute Data", ++ // Short description. ++ "This utility provides several imputation strategies for missing data. " ++ "Given a dataset with missing values, this can impute according to several " ++ "strategies, including user-defined values.", ++ // Long description. ++ "This utility takes a dataset and converts a user-defined missing variable " ++ "to another to provide more meaningful analysis." + "\n\n" + "The program does not modify the original file, but instead makes a " + "separate file to save the output data; You can save the output by " +@@ -35,7 +41,10 @@ PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " + "column-wise dataset, and save the result to result.csv, we could run" + "\n\n" + "$ mlpack_preprocess_imputer -i dataset.csv -o result.csv -m NULL -d 0 \n" +- "> -s listwise_deletion"); ++ "> -s listwise_deletion", ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + PARAM_STRING_IN_REQ("input_file", "File containing data.", "i"); + PARAM_STRING_OUT("output_file", "File to save output into.", "o"); +diff --git a/src/mlpack/methods/preprocess/preprocess_split_main.cpp b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +index a66056012..5fafd05c3 100644 +--- a/src/mlpack/methods/preprocess/preprocess_split_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +@@ -15,11 +15,16 @@ + #include + #include + +-PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " +- "and splits them into a training set and a test set. Before the split, the " +- "points in the dataset are randomly reordered. The percentage of the " +- "dataset to be used as the test set can be specified with the " + +- PRINT_PARAM_STRING("test_ratio") + " parameter; the default is 0.2 (20%)." ++PROGRAM_INFO("Split Data", ++ // Short description. ++ "A utility to split data into a training and testing dataset. This can " ++ "also split labels according to the same split.", ++ // Long description. ++ "This utility takes a dataset and optionally labels and splits them into a " ++ "training set and a test set. Before the split, the points in the dataset " ++ "are randomly reordered. The percentage of the dataset to be used as the " ++ "test set can be specified with the " + PRINT_PARAM_STRING("test_ratio") + ++ " parameter; the default is 0.2 (20%)." + "\n\n" + "The output training and test matrices may be saved with the " + + PRINT_PARAM_STRING("training") + " and " + PRINT_PARAM_STRING("test") + +@@ -48,7 +53,10 @@ PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " + "\n\n" + + PRINT_CALL("preprocess_split", "input", "X", "input_labels", "y", + "test_ratio", 0.3, "training", "X_train", "training_labels", "y_train", +- "test", "X_test", "test_labels", "y_test")); ++ "test", "X_test", "test_labels", "y_test"), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data.", "i"); +diff --git a/src/mlpack/methods/radical/CMakeLists.txt b/src/mlpack/methods/radical/CMakeLists.txt +index 886566b11..c7dc29cac 100644 +--- a/src/mlpack/methods/radical/CMakeLists.txt ++++ b/src/mlpack/methods/radical/CMakeLists.txt +@@ -15,3 +15,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(radical) + add_python_binding(radical) ++add_markdown_docs(radical "cli;python" "transformations") +diff --git a/src/mlpack/methods/radical/radical_main.cpp b/src/mlpack/methods/radical/radical_main.cpp +index 7d5e59447..2de949635 100644 +--- a/src/mlpack/methods/radical/radical_main.cpp ++++ b/src/mlpack/methods/radical/radical_main.cpp +@@ -15,11 +15,18 @@ + #include + #include "radical.hpp" + +-PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" +- "component analysis (ICA). Assuming that we have an input matrix X, the" +- "goal is to find a square unmixing matrix W such that Y = W * X and the " +- "dimensions of Y are independent components. If the algorithm is running" +- "particularly slowly, try reducing the number of replicates." ++PROGRAM_INFO("RADICAL", ++ // Short description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Given a dataset, this can decompose the dataset into an unmixing " ++ "matrix and an independent component matrix; this can be useful for " ++ "preprocessing.", ++ // Long description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Assuming that we have an input matrix X, the goal is to find a " ++ "square unmixing matrix W such that Y = W * X and the dimensions of Y are " ++ "independent components. If the algorithm is running particularly slowly, " ++ "try reducing the number of replicates." + "\n\n" + "The input matrix to perform ICA on should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter. The output matrix Y may be " +@@ -31,7 +38,14 @@ PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" + "40 replicates, saving the independent components to " + + PRINT_DATASET("ic") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic")); ++ PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic"), ++ SEE_ALSO("Independent component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Independent_component_analysis"), ++ SEE_ALSO("ICA using spacings estimates of entropy (pdf)", ++ "http://www.jmlr.org/papers/volume4/learned-miller03a/" ++ "learned-miller03a.pdf"), ++ SEE_ALSO("mlpack::radical::Radical C++ class documentation", ++ "@doxygen/classmlpack_1_1radical_1_1Radical.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset for ICA.", "i"); + +diff --git a/src/mlpack/methods/random_forest/CMakeLists.txt b/src/mlpack/methods/random_forest/CMakeLists.txt +index 3ac70de8a..89b03837d 100644 +--- a/src/mlpack/methods/random_forest/CMakeLists.txt ++++ b/src/mlpack/methods/random_forest/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(random_forest) + add_python_binding(random_forest) ++add_markdown_docs(random_forest "cli;python" "classification") +diff --git a/src/mlpack/methods/random_forest/random_forest_main.cpp b/src/mlpack/methods/random_forest/random_forest_main.cpp +index 2ca69b853..4915f1cd5 100644 +--- a/src/mlpack/methods/random_forest/random_forest_main.cpp ++++ b/src/mlpack/methods/random_forest/random_forest_main.cpp +@@ -20,6 +20,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Random forests", ++ // Short description. ++ "An implementation of the standard random forest algorithm by Leo Breiman " ++ "for classification. Given labeled data, a random forest can be trained " ++ "and saved for future use; or, a pre-trained random forest can be used for " ++ "classification.", ++ // Long description. + "This program is an implementation of the standard random forest " + "classification algorithm by Leo Breiman. A random forest can be " + "trained and saved for later use, or a random forest may be loaded " +@@ -70,7 +76,16 @@ PROGRAM_INFO("Random forests", + "could call " + "\n\n" + + PRINT_CALL("random_forest", "input_model", "rf_model", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@hoeffding_tree", "#hoeffding_tree"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("Random forest on Wikipedia", ++ "https://en.wikipedia.org/wiki/Random_forest"), ++ SEE_ALSO("Random forests (pdf)", ++ "https://link.springer.com/content/pdf/10.1023/A:1010933404324.pdf"), ++ SEE_ALSO("mlpack::tree::RandomForest C++ class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1RandomForest.html")); + + PARAM_MATRIX_IN("training", "Training dataset.", "t"); + PARAM_UROW_IN("labels", "Labels for training dataset.", "l"); +diff --git a/src/mlpack/methods/range_search/CMakeLists.txt b/src/mlpack/methods/range_search/CMakeLists.txt +index 9d29343db..68a020315 100644 +--- a/src/mlpack/methods/range_search/CMakeLists.txt ++++ b/src/mlpack/methods/range_search/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(range_search) + #add_python_binding(range_search) ++add_markdown_docs(range_search "cli" "geometry") +diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp +index 27e49caf1..c50bb709b 100644 +--- a/src/mlpack/methods/range_search/range_search_main.cpp ++++ b/src/mlpack/methods/range_search/range_search_main.cpp +@@ -29,6 +29,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("Range Search", ++ // Short description. ++ "An implementation of range search with single-tree and dual-tree " ++ "algorithms. Given a set of reference points and a set of query points and" ++ " a range, this can find the set of reference points within the desired " ++ "range for each query point, and any trees built during the computation can" ++ " be saved for reuse with future range searches.", ++ // Long description. + "This program implements range search with a Euclidean distance metric. " + "For a given query point, a given range, and a given set of reference " + "points, the program will return all of the reference points with distance " +@@ -55,7 +62,14 @@ PROGRAM_INFO("Range Search", + " resultant CSV-like files may not be loadable by many programs. However, " + "at this time a better way to store this non-square result is not known. " + "As a result, any output files will be written as CSVs in this manner, " +- "regardless of the given extension."); ++ "regardless of the given extension.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Range searching on Wikipedia", ++ "https://en.wikipedia.org/wiki/Range_searching"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::range::RangeSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1range_1_1RangeSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/rann/CMakeLists.txt b/src/mlpack/methods/rann/CMakeLists.txt +index 457a11dd6..3ba78994a 100644 +--- a/src/mlpack/methods/rann/CMakeLists.txt ++++ b/src/mlpack/methods/rann/CMakeLists.txt +@@ -37,3 +37,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # reference sets. + add_cli_executable(krann) + add_python_binding(krann) ++add_markdown_docs(krann "cli;python" "geometry") +diff --git a/src/mlpack/methods/rann/krann_main.cpp b/src/mlpack/methods/rann/krann_main.cpp +index 8224a2113..94661a53f 100644 +--- a/src/mlpack/methods/rann/krann_main.cpp ++++ b/src/mlpack/methods/rann/krann_main.cpp +@@ -30,6 +30,13 @@ typedef RAModel RANNModel; + + // Information about the program itself. + PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", ++ // Short description. ++ "An implementation of rank-approximate k-nearest-neighbor search (kRANN) " ++ " using single-tree and dual-tree algorithms. Given a set of reference " ++ "points and query points, this can find the k nearest neighbors in the " ++ "reference set of each query point using trees; trees that are built can " ++ "be saved for future use.", ++ // Long description. + "This program will calculate the k rank-approximate-nearest-neighbors of a " + "set of points. You may specify a separate set of reference points and " + "query points, or just a reference set which will be used as both the " +@@ -55,7 +62,15 @@ PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", + "neighbors output file corresponds to the index of the point in the " + "reference set which is the i'th nearest neighbor from the point in the " + "query set with index j. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("Rank-approximate nearest neighbor search: Retaining meaning and " ++ "speed in high dimensions (pdf)", "https://papers.nips.cc/paper/3864-" ++ "rank-approximate-nearest-neighbor-search-retaining-meaning-and-speed-" ++ "in-high-dimensions.pdf"), ++ SEE_ALSO("mlpack::neighbor::RASearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1RASearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/softmax_regression/CMakeLists.txt b/src/mlpack/methods/softmax_regression/CMakeLists.txt +index c6770f7c4..60a7d4cff 100644 +--- a/src/mlpack/methods/softmax_regression/CMakeLists.txt ++++ b/src/mlpack/methods/softmax_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(softmax_regression) + add_python_binding(softmax_regression) ++add_markdown_docs(softmax_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +index 97b54a2a5..4e3f88e76 100644 +--- a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp ++++ b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +@@ -24,11 +24,18 @@ using namespace mlpack::regression; + using namespace mlpack::util; + + // Define parameters for the executable. +-PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " +- "a generalization of logistic regression to the multiclass case, and has " +- "support for L2 regularization. The program is able to train a model, load" +- " an existing model, and give predictions (and optionally their accuracy) " +- "for test data." ++PROGRAM_INFO("Softmax Regression", ++ // Short description. ++ "An implementation of softmax regression for classification, which is a " ++ "multiclass generalization of logistic regression. Given labeled data, a " ++ "softmax regression model can be trained and saved for future use, or, a " ++ "pre-trained softmax regression model can be used for classification of " ++ "new points.", ++ // Long description. ++ "This program performs softmax regression, a generalization of logistic " ++ "regression to the multiclass case, and has support for L2 regularization. " ++ " The program is able to train a model, load an existing model, and give " ++ "predictions (and optionally their accuracy) for test data." + "\n\n" + "Training a softmax regression model is done by giving a file of training " + "points with the " + PRINT_PARAM_STRING("training") + " parameter and their" +@@ -71,7 +78,14 @@ PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " + " " + PRINT_DATASET("predictions") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("softmax_regression", "input_model", "sr_model", "test", +- "test_points", "predictions", "predictions")); ++ "test_points", "predictions", "predictions"), ++ SEE_ALSO("@logistic_regression", "#logistic_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Multinomial logistic regression (softmax regression) on " ++ "Wikipedia", ++ "https://en.wikipedia.org/wiki/Multinomial_logistic_regression"), ++ SEE_ALSO("mlpack::regression::SoftmaxRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1SoftmaxRegression.html")); + + // Required options. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/sparse_coding/CMakeLists.txt b/src/mlpack/methods/sparse_coding/CMakeLists.txt +index cfd1108cb..7bb0f7b79 100644 +--- a/src/mlpack/methods/sparse_coding/CMakeLists.txt ++++ b/src/mlpack/methods/sparse_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(sparse_coding) + add_python_binding(sparse_coding) ++add_markdown_docs(sparse_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +index e4b561f53..515cb78eb 100644 +--- a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp ++++ b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +@@ -22,12 +22,20 @@ using namespace mlpack::math; + using namespace mlpack::sparse_coding; + using namespace mlpack::util; + +-PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " +- "Dictionary Learning, which achieves sparsity via an l1-norm regularizer on" +- " the codes (LASSO) or an (l1+l2)-norm regularizer on the codes (the " +- "Elastic Net). Given a dense data matrix X with d dimensions and n points," +- " sparse coding seeks to find a dense dictionary matrix D with k atoms in " +- "d dimensions, and a sparse coding matrix Z with n points in k dimensions." ++PROGRAM_INFO("Sparse Coding", ++ // Short description. ++ "An implementation of Sparse Coding with Dictionary Learning. Given a " ++ "dataset, this will decompose the dataset into a sparse combination of a " ++ "few dictionary elements, where the dictionary is learned during " ++ "computation; a dictionary can be reused for future sparse coding of new " ++ "points.", ++ // Long description. ++ "An implementation of Sparse Coding with Dictionary Learning, which " ++ "achieves sparsity via an l1-norm regularizer on the codes (LASSO) or an " ++ "(l1+l2)-norm regularizer on the codes (the Elastic Net). Given a dense " ++ "data matrix X with d dimensions and n points, sparse coding seeks to find " ++ "a dense dictionary matrix D with k atoms in d dimensions, and a sparse " ++ "coding matrix Z with n points in k dimensions." + "\n\n" + "The original data matrix X can then be reconstructed as Z * D. Therefore," + " this program finds a representation of each point in X as a sparse linear" +@@ -62,7 +70,18 @@ PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " + PRINT_DATASET("codes") + ": " + "\n\n" + + PRINT_CALL("sparse_coding", "input_model", "model", "test", "otherdata", +- "codes", "codes")); ++ "codes", "codes"), ++ SEE_ALSO("@local_coordinate_coding", "#local_coordinate_coding"), ++ SEE_ALSO("Sparse dictionary learning on Wikipedia", ++ "https://en.wikipedia.org/wiki/Sparse_dictionary_learning"), ++ SEE_ALSO("Efficient sparse coding algorithms (pdf)", ++ "http://papers.nips.cc/paper/2979-efficient-sparse-coding-" ++ "algorithms.pdf"), ++ SEE_ALSO("Regularization and variable selection via the elastic net", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4696&" ++ "rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::sparse_coding::SparseCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1sparse__coding_1_1SparseCoding.html")); + + // Train the model. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +-- +2.20.1 + diff --git a/_src/markdown-patches/mlpack-3.0.3-markdown.patch b/_src/markdown-patches/mlpack-3.0.3-markdown.patch new file mode 100644 index 000000000..c0b8359b7 --- /dev/null +++ b/_src/markdown-patches/mlpack-3.0.3-markdown.patch @@ -0,0 +1,7189 @@ +From 6abbb6677379719c965dafba960035a091261171 Mon Sep 17 00:00:00 2001 +From: Ryan Curtin +Date: Wed, 6 Mar 2019 19:56:30 -0500 +Subject: [PATCH] Backport Markdown support onto mlpack 3.0.3. + +--- + CMake/RunProgram.cmake | 8 + + CMakeLists.txt | 14 + + src/mlpack/CMakeLists.txt | 9 +- + src/mlpack/bindings/CMakeLists.txt | 2 + + src/mlpack/bindings/cli/CMakeLists.txt | 2 + + .../bindings/cli/default_param_impl.hpp | 59 +- + src/mlpack/bindings/cli/end_program.hpp | 2 +- + .../bindings/cli/get_printable_type.hpp | 79 +++ + .../bindings/cli/get_printable_type_impl.hpp | 109 ++++ + .../bindings/cli/print_doc_functions.hpp | 38 ++ + .../bindings/cli/print_doc_functions_impl.hpp | 110 +++- + src/mlpack/bindings/cli/print_help.cpp | 6 +- + src/mlpack/bindings/cli/print_type_doc.hpp | 81 +++ + .../bindings/cli/print_type_doc_impl.hpp | 173 ++++++ + src/mlpack/bindings/cli/string_type_param.hpp | 6 - + .../bindings/cli/string_type_param_impl.hpp | 10 - + src/mlpack/bindings/markdown/CMakeLists.txt | 209 +++++++ + .../markdown/MarkdownCategories.cmake | 11 + + src/mlpack/bindings/markdown/binding_info.cpp | 49 ++ + src/mlpack/bindings/markdown/binding_info.hpp | 62 ++ + .../bindings/markdown/default_param.hpp | 45 ++ + .../markdown/generate_markdown.binding.cpp.in | 46 ++ + .../markdown/generate_markdown.binding.hpp.in | 28 + + .../markdown/generate_markdown.cpp.in | 114 ++++ + .../bindings/markdown/get_binding_name.cpp | 40 ++ + .../bindings/markdown/get_binding_name.hpp | 30 + + src/mlpack/bindings/markdown/get_param.hpp | 38 ++ + .../bindings/markdown/get_printable_param.hpp | 126 ++++ + .../markdown/get_printable_param_name.hpp | 83 +++ + .../get_printable_param_name_impl.hpp | 79 +++ + .../markdown/get_printable_param_value.hpp | 88 +++ + .../get_printable_param_value_impl.hpp | 84 +++ + .../bindings/markdown/get_printable_type.hpp | 62 ++ + .../bindings/markdown/is_serializable.hpp | 63 ++ + src/mlpack/bindings/markdown/md_option.hpp | 109 ++++ + .../bindings/markdown/print_doc_functions.hpp | 109 ++++ + .../markdown/print_doc_functions_impl.hpp | 540 ++++++++++++++++++ + src/mlpack/bindings/markdown/print_docs.cpp | 218 +++++++ + src/mlpack/bindings/markdown/print_docs.hpp | 33 ++ + .../bindings/markdown/print_type_doc.hpp | 46 ++ + .../bindings/markdown/program_doc_wrapper.hpp | 41 ++ + .../bindings/markdown/res/change_language.js | 102 ++++ + .../bindings/markdown/res/formatting.css | 142 +++++ + src/mlpack/bindings/markdown/res/menu_bg.png | Bin 0 -> 395 bytes + src/mlpack/bindings/python/CMakeLists.txt | 4 + + src/mlpack/bindings/python/default_param.hpp | 95 +++ + .../bindings/python/default_param_impl.hpp | 147 +++++ + .../bindings/python/get_cython_type.hpp | 10 - + .../bindings/python/get_printable_type.hpp | 120 ++++ + .../python/get_printable_type_impl.hpp | 151 +++++ + src/mlpack/bindings/python/print_doc.hpp | 22 +- + .../bindings/python/print_doc_functions.hpp | 26 + + .../python/print_doc_functions_impl.hpp | 124 +++- + .../python/print_input_processing.hpp | 1 - + src/mlpack/bindings/python/print_type_doc.hpp | 81 +++ + .../bindings/python/print_type_doc_impl.hpp | 162 ++++++ + src/mlpack/bindings/python/py_option.hpp | 4 + + .../python/tests/test_python_binding_main.cpp | 1 + + src/mlpack/core/util/cli.cpp | 3 +- + src/mlpack/core/util/mlpack_main.hpp | 68 ++- + src/mlpack/core/util/param.hpp | 31 +- + src/mlpack/core/util/program_doc.cpp | 30 +- + src/mlpack/core/util/program_doc.hpp | 17 +- + src/mlpack/methods/CMakeLists.txt | 4 + + src/mlpack/methods/adaboost/CMakeLists.txt | 1 + + src/mlpack/methods/adaboost/adaboost_main.cpp | 21 +- + src/mlpack/methods/approx_kfn/CMakeLists.txt | 1 + + .../methods/approx_kfn/approx_kfn_main.cpp | 19 +- + src/mlpack/methods/cf/CMakeLists.txt | 1 + + src/mlpack/methods/cf/cf_main.cpp | 25 +- + src/mlpack/methods/dbscan/CMakeLists.txt | 1 + + src/mlpack/methods/dbscan/dbscan_main.cpp | 12 +- + .../methods/decision_stump/CMakeLists.txt | 1 + + .../decision_stump/decision_stump_main.cpp | 12 +- + .../methods/decision_tree/CMakeLists.txt | 1 + + .../decision_tree/decision_tree_main.cpp | 16 +- + src/mlpack/methods/det/CMakeLists.txt | 1 + + src/mlpack/methods/det/det_main.cpp | 15 +- + src/mlpack/methods/emst/CMakeLists.txt | 1 + + src/mlpack/methods/emst/emst_main.cpp | 13 +- + src/mlpack/methods/fastmks/CMakeLists.txt | 1 + + src/mlpack/methods/fastmks/fastmks_main.cpp | 15 +- + src/mlpack/methods/gmm/CMakeLists.txt | 5 + + src/mlpack/methods/gmm/gmm_generate_main.cpp | 12 +- + .../methods/gmm/gmm_probability_main.cpp | 13 +- + src/mlpack/methods/gmm/gmm_train_main.cpp | 13 +- + src/mlpack/methods/hmm/CMakeLists.txt | 7 + + src/mlpack/methods/hmm/hmm_generate_main.cpp | 18 +- + src/mlpack/methods/hmm/hmm_loglik_main.cpp | 19 +- + src/mlpack/methods/hmm/hmm_train_main.cpp | 21 +- + src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 19 +- + .../methods/hoeffding_trees/CMakeLists.txt | 1 + + .../hoeffding_trees/hoeffding_tree_main.cpp | 14 +- + src/mlpack/methods/kernel_pca/CMakeLists.txt | 1 + + .../methods/kernel_pca/kernel_pca_main.cpp | 17 +- + src/mlpack/methods/kmeans/CMakeLists.txt | 1 + + src/mlpack/methods/kmeans/kmeans_main.cpp | 32 +- + src/mlpack/methods/lars/CMakeLists.txt | 1 + + src/mlpack/methods/lars/lars_main.cpp | 21 +- + .../methods/linear_regression/CMakeLists.txt | 1 + + .../linear_regression_main.cpp | 14 +- + .../local_coordinate_coding/CMakeLists.txt | 1 + + .../local_coordinate_coding_main.cpp | 14 +- + .../logistic_regression/CMakeLists.txt | 1 + + .../logistic_regression_main.cpp | 17 +- + src/mlpack/methods/lsh/CMakeLists.txt | 1 + + src/mlpack/methods/lsh/lsh_main.cpp | 17 +- + src/mlpack/methods/mean_shift/CMakeLists.txt | 1 + + .../methods/mean_shift/mean_shift_main.cpp | 23 +- + src/mlpack/methods/naive_bayes/CMakeLists.txt | 1 + + src/mlpack/methods/naive_bayes/nbc_main.cpp | 14 +- + src/mlpack/methods/nca/CMakeLists.txt | 1 + + src/mlpack/methods/nca/nca_main.cpp | 15 +- + .../methods/neighbor_search/CMakeLists.txt | 3 + + .../methods/neighbor_search/kfn_main.cpp | 14 +- + .../methods/neighbor_search/knn_main.cpp | 17 +- + src/mlpack/methods/nmf/CMakeLists.txt | 1 + + src/mlpack/methods/nmf/nmf_main.cpp | 25 +- + src/mlpack/methods/pca/CMakeLists.txt | 1 + + src/mlpack/methods/pca/pca_main.cpp | 24 +- + src/mlpack/methods/perceptron/CMakeLists.txt | 1 + + .../methods/perceptron/perceptron_main.cpp | 13 +- + src/mlpack/methods/preprocess/CMakeLists.txt | 7 + + .../preprocess/preprocess_binarize_main.cpp | 13 +- + .../preprocess/preprocess_describe_main.cpp | 22 +- + .../preprocess/preprocess_imputer_main.cpp | 15 +- + .../preprocess/preprocess_split_main.cpp | 20 +- + src/mlpack/methods/radical/CMakeLists.txt | 1 + + src/mlpack/methods/radical/radical_main.cpp | 26 +- + .../methods/random_forest/CMakeLists.txt | 1 + + .../random_forest/random_forest_main.cpp | 17 +- + .../methods/range_search/CMakeLists.txt | 1 + + .../range_search/range_search_main.cpp | 16 +- + src/mlpack/methods/rann/CMakeLists.txt | 1 + + src/mlpack/methods/rann/krann_main.cpp | 17 +- + .../methods/softmax_regression/CMakeLists.txt | 1 + + .../softmax_regression_main.cpp | 26 +- + .../methods/sparse_coding/CMakeLists.txt | 1 + + .../sparse_coding/sparse_coding_main.cpp | 33 +- + 139 files changed, 5011 insertions(+), 205 deletions(-) + create mode 100644 CMake/RunProgram.cmake + create mode 100644 src/mlpack/bindings/cli/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/cli/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/CMakeLists.txt + create mode 100644 src/mlpack/bindings/markdown/MarkdownCategories.cmake + create mode 100644 src/mlpack/bindings/markdown/binding_info.cpp + create mode 100644 src/mlpack/bindings/markdown/binding_info.hpp + create mode 100644 src/mlpack/bindings/markdown/default_param.hpp + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.cpp.in + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.cpp + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/markdown/is_serializable.hpp + create mode 100644 src/mlpack/bindings/markdown/md_option.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.cpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.hpp + create mode 100644 src/mlpack/bindings/markdown/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/markdown/program_doc_wrapper.hpp + create mode 100644 src/mlpack/bindings/markdown/res/change_language.js + create mode 100644 src/mlpack/bindings/markdown/res/formatting.css + create mode 100644 src/mlpack/bindings/markdown/res/menu_bg.png + create mode 100644 src/mlpack/bindings/python/default_param.hpp + create mode 100644 src/mlpack/bindings/python/default_param_impl.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc_impl.hpp + +diff --git a/CMake/RunProgram.cmake b/CMake/RunProgram.cmake +new file mode 100644 +index 000000000..6ae385314 +--- /dev/null ++++ b/CMake/RunProgram.cmake +@@ -0,0 +1,8 @@ ++# RunProgram.cmake: a CMake script that actually runs the given program to ++# generate a file, which is output into the given directory. ++# ++# This script depends on the following arguments: ++# ++# PROGRAM: the program to run to. ++# OUTPUT_FILE: the file to store the output in. ++execute_process(COMMAND ${PROGRAM} OUTPUT_FILE ${OUTPUT_FILE}) +diff --git a/CMakeLists.txt b/CMakeLists.txt +index bf5c8a184..512415116 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -15,6 +15,20 @@ option(BUILD_CLI_EXECUTABLES "Build command-line executables." ON) + option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) + option(BUILD_SHARED_LIBS + "Compile shared libraries (if OFF, static libraries are compiled)." ON) ++ ++# Currently Python bindings aren't known to build successfully on Windows, so ++# set BUILD_PYTHON_BINDINGS to OFF when the platform is Windows. ++if (WIN32) ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." OFF) ++ message(WARNING "By default Python bindings are not compiled for Windows because they are not known to work. Set BUILD_PYTHON_BINDINGS to ON if you want them built.") ++else () ++ option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) ++endif() ++ ++# Build Markdown bindings for documentation. This is used as part of website ++# generation. ++option(BUILD_MARKDOWN_BINDINGS "Build Markdown bindings for website documentation." OFF) ++ + option(BUILD_WITH_COVERAGE + "Build with support for code coverage tools (gcc only)." OFF) + option(MATHJAX +diff --git a/src/mlpack/CMakeLists.txt b/src/mlpack/CMakeLists.txt +index 29c545c9e..b555e0100 100644 +--- a/src/mlpack/CMakeLists.txt ++++ b/src/mlpack/CMakeLists.txt +@@ -14,7 +14,7 @@ set(DIRS + ) + + foreach(dir ${DIRS}) +- add_subdirectory(${dir}) ++ add_subdirectory(${dir}) + endforeach() + + # MLPACK_SRCS is set in the subdirectories. The dependencies (MLPACK_LIBRARIES) +@@ -40,8 +40,7 @@ if (NOT BUILD_SHARED_LIBS) + add_definitions(-DMLPACK_STATIC_DEFINE) + endif () + +-target_link_libraries(mlpack +- ${MLPACK_LIBRARIES}) ++target_link_libraries(mlpack ${MLPACK_LIBRARIES}) + + set_target_properties(mlpack + PROPERTIES +@@ -122,3 +121,7 @@ if (BUILD_PYTHON_BINDINGS) + configure_file(${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/setup.py.in + ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py) + endif () ++ ++# If we are building Markdown documentation, we have to run some setup after we ++# recurse into methods/. If not, this function is empty. ++post_markdown_setup() +diff --git a/src/mlpack/bindings/CMakeLists.txt b/src/mlpack/bindings/CMakeLists.txt +index f35249ddd..e726c515b 100644 +--- a/src/mlpack/bindings/CMakeLists.txt ++++ b/src/mlpack/bindings/CMakeLists.txt +@@ -1,6 +1,7 @@ + # All we have to do is recurse into the subdirectories. + set(DIRS + cli ++ markdown + python + tests + ) +@@ -9,6 +10,7 @@ foreach(dir ${DIRS}) + add_subdirectory(${dir}) + endforeach() + ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) + set(MLPACK_SRCS ${MLPACK_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) + set(DISABLE_CFLAGS ${DISABLE_CFLAGS} PARENT_SCOPE) +diff --git a/src/mlpack/bindings/cli/CMakeLists.txt b/src/mlpack/bindings/cli/CMakeLists.txt +index 83c50292e..280043fa6 100644 +--- a/src/mlpack/bindings/cli/CMakeLists.txt ++++ b/src/mlpack/bindings/cli/CMakeLists.txt +@@ -25,6 +25,8 @@ set(SOURCES + print_doc_functions_impl.hpp + print_help.hpp + print_help.cpp ++ print_type_doc.hpp ++ print_type_doc_impl.hpp + set_param.hpp + string_type_param.hpp + string_type_param_impl.hpp +diff --git a/src/mlpack/bindings/cli/default_param_impl.hpp b/src/mlpack/bindings/cli/default_param_impl.hpp +index 8c1740c1f..8c9ea7896 100644 +--- a/src/mlpack/bindings/cli/default_param_impl.hpp ++++ b/src/mlpack/bindings/cli/default_param_impl.hpp +@@ -32,7 +32,9 @@ std::string DefaultParamImpl( + std::tuple>>::type* /* junk */) + { + std::ostringstream oss; +- oss << boost::any_cast(data.value); ++ if (!std::is_same::value) ++ oss << boost::any_cast(data.value); ++ + return oss.str(); + } + +@@ -48,9 +50,35 @@ std::string DefaultParamImpl( + std::ostringstream oss; + const T& vector = boost::any_cast(data.value); + oss << "["; +- for (size_t i = 0; i < vector.size() - 1; ++i) +- oss << vector[i] << " "; +- oss << vector[vector.size() - 1] << "]"; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ + return oss.str(); + } + +@@ -67,39 +95,30 @@ std::string DefaultParamImpl( + } + + /** +- * Return the default value of a matrix option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a matrix option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::enable_if_c< + arma::is_arma_type::value || + std::is_same>::value>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ // The filename will always be empty. ++ return "''"; + } + + /** +- * Return the default value of a model option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a model option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::disable_if>::type* /* junk */, + const typename boost::enable_if>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ return "''"; + } + + +diff --git a/src/mlpack/bindings/cli/end_program.hpp b/src/mlpack/bindings/cli/end_program.hpp +index 031c49d5f..8e412db10 100644 +--- a/src/mlpack/bindings/cli/end_program.hpp ++++ b/src/mlpack/bindings/cli/end_program.hpp +@@ -50,7 +50,7 @@ inline void EndProgram() + while (it != parameters.end()) + { + // Now, figure out what type it is, and print it. +- // We can handle strings, ints, bools, floats, doubles. ++ // We can handle strings, ints, bools, doubles. + const util::ParamData& data = it->second; + std::string boostName; + CLI::GetSingleton().functionMap[data.tname]["MapParameterName"](data, +diff --git a/src/mlpack/bindings/cli/get_printable_type.hpp b/src/mlpack/bindings/cli/get_printable_type.hpp +new file mode 100644 +index 000000000..c4634c4ad +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/get_printable_type_impl.hpp b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..bb1ff85a5 +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ if (std::is_same::value) ++ return "flag"; ++ else if (std::is_same::value) ++ return "int"; ++ else if (std::is_same::value) ++ return "double"; ++ else if (std::is_same::value) ++ return "string"; ++ else ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ return "int vector"; ++ else if (std::is_same>::value) ++ return "string vector"; ++ else ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ return "2-d matrix file"; ++ else if (std::is_same>::value) ++ return "2-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else ++ throw std::invalid_argument("unknown Armadillo type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "2-d categorical matrix file"; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return data.cppType + " file"; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_doc_functions.hpp b/src/mlpack/bindings/cli/print_doc_functions.hpp +index cfc024b2c..81ce73816 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions.hpp +@@ -20,12 +20,38 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ ++/** ++ * Print documentation for each of the types. ++ */ ++inline std::string PrintTypeDocs(); ++ + /** + * Given a parameter type, print the corresponding value. + */ + template + inline std::string PrintValue(const T& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -36,6 +62,12 @@ inline std::string PrintDataset(const std::string& dataset); + */ + inline std::string PrintModel(const std::string& model); + ++/** ++ * Print the type of a parameter that a user would specify from the ++ * command-line. ++ */ ++inline std::string PrintType(const util::ParamData& param); ++ + /** + * Base case for recursion. + */ +@@ -56,6 +88,12 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +index 0f37e7d5e..96349f5dd 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +@@ -20,6 +20,31 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ return "mlpack_" + bindingName; ++} ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& /* bindingName */) ++{ ++ return ""; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return ""; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -35,6 +60,23 @@ inline std::string PrintValue(const T& value, bool quotes) + return oss.str(); + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -107,10 +149,76 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args) + { +- return util::HyphenateString("$ " + programName + " " + ++ return util::HyphenateString("$ " + GetBindingName(programName) + " " + + ProcessOptions(args...), 2); + } + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << "$ " << GetBindingName(programName); ++ ++ // Handle all options---first input options, then output options. ++ const std::map& parameters = CLI::Parameters(); ++ ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " "; ++ if (!it->second.required) ++ oss << "["; ++ ++ oss << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ ++ if (!it->second.required) ++ oss << "]"; ++ } ++ ++ // Now get the output options. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " [" << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ oss << "]"; ++ } ++ ++ return util::HyphenateString(oss.str(), 8); ++} ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_help.cpp b/src/mlpack/bindings/cli/print_help.cpp +index c6ef6c90d..ff62a7154 100644 +--- a/src/mlpack/bindings/cli/print_help.cpp ++++ b/src/mlpack/bindings/cli/print_help.cpp +@@ -118,7 +118,11 @@ void PrintHelp(const std::string& param) + } + + // Append default value to description. +- if (pass >= 1 && data.cppType != "bool") ++ if (pass >= 1 && (data.cppType == "int" || data.cppType == "double" || ++ data.cppType == "std::string" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector")) + { + std::string defaultValue; + CLI::GetSingleton().functionMap[data.tname]["DefaultParam"](data, +diff --git a/src/mlpack/bindings/cli/print_type_doc.hpp b/src/mlpack/bindings/cli/print_type_doc.hpp +new file mode 100644 +index 000000000..677854e60 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_type_doc_impl.hpp b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..3a5a20912 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +@@ -0,0 +1,173 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option. If not specified, it is false; if " ++ "specified, it is true."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A vector of integers, separated by commas (i.e., \"1,2,3\")."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A vector of strings, separated by commas (i.e., " ++ "\"hello\",\"goodbye\")."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ return "A data matrix filename. The file can be CSV (.csv), TSV (.csv), " ++ "ASCII (space-separated values, .txt), Armadillo ASCII (.txt), PGM " ++ "(.pgm), PPM (.ppm), Armadillo binary (.bin), or HDF5 (.h5, .hdf, " ++ ".hdf5, or .he5), if mlpack was compiled with HDF5 support. The type " ++ "of the data is detected by the extension of the filename. The storage" ++ " should be such that one row corresponds to one point, and one column " ++ "corresponds to one dimension (this is the typical storage format for " ++ "on-disk data). All values of the matrix will be loaded as double-" ++ "precision floating point data."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A data matrix filename, where the matrix holds only non-negative " ++ "integer values. This type is often used for labels or indices. The " ++ "file can be CSV (.csv), TSV (.csv), ASCII (space-separated values, " ++ ".txt), Armadillo ASCII (.txt), PGM (.pgm), PPM (.ppm), Armadillo " ++ "binary (.bin), or HDF5 (.h5, .hdf, .hdf5, or .he5), if mlpack was " ++ "compiled with HDF5 support. The type of the data is detected by the " ++ "extension of the filename. The storage should be such that one row " ++ "corresponds to one point, and one column corresponds to one dimension " ++ "(this is the typical storage format for on-disk data). All values of " ++ "the matrix will be loaded as unsigned integers."; ++ } ++ else if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "A one-dimensional vector filename. This file can take the same " ++ "formats as the data matrix filenames; however, it must either contain " ++ "one row and many columns, or one column and many rows."; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "A one-dimensional vector filename, where the matrix holds only non-" ++ "negative integer values. This type is typically used for labels or " ++ "predictions or other indices. This file can take the same formats as " ++ "the data matrix filenames; however, it must either contain one row and" ++ " many columns, or one column and many rows."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A filename for a data matrix that can contain categorical " ++ "(non-numeric) data. If the file contains only numeric data, then the " ++ "same formats for regular data matrices can be used. If the file " ++ "contains strings or other values that can't be parsed as numbers, then " ++ "the type to be loaded must be CSV (.csv) or ARFF (.arff). Any non-" ++ "numeric data will be converted to an unsigned integer value, and " ++ "dimensions where the data is converted will be treated as categorical " ++ "dimensions. When using this format, there is no need for one-hot " ++ "encoding of categorical data."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "A filename containing an mlpack model. These can have one of three " ++ "formats: binary (.bin), text (.txt), and XML (.xml). The XML format " ++ "produces the largest (but most human-readable) files, while the binary " ++ "format can be significantly more compact and quicker to load and save."; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/string_type_param.hpp b/src/mlpack/bindings/cli/string_type_param.hpp +index 28caa8991..702fb4668 100644 +--- a/src/mlpack/bindings/cli/string_type_param.hpp ++++ b/src/mlpack/bindings/cli/string_type_param.hpp +@@ -74,12 +74,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + const void* /* input */, + void* output); + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output); +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/cli/string_type_param_impl.hpp b/src/mlpack/bindings/cli/string_type_param_impl.hpp +index 01065086e..86fb96302 100644 +--- a/src/mlpack/bindings/cli/string_type_param_impl.hpp ++++ b/src/mlpack/bindings/cli/string_type_param_impl.hpp +@@ -80,16 +80,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + *outstr = "string"; + } + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output) +-{ +- std::string* outstr = (std::string*) output; +- *outstr = "float"; +-} +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/markdown/CMakeLists.txt b/src/mlpack/bindings/markdown/CMakeLists.txt +new file mode 100644 +index 000000000..b6b31cfba +--- /dev/null ++++ b/src/mlpack/bindings/markdown/CMakeLists.txt +@@ -0,0 +1,209 @@ ++macro (not_found_return message) ++ message(STATUS "${message}") ++ ++ macro (add_markdown_docs name languages category) ++ # Do nothing. ++ endmacro() ++ ++ function (post_markdown_setup) ++ # Do nothing. ++ endfunction () ++ ++ return () ++endmacro () ++ ++if (NOT BUILD_MARKDOWN_BINDINGS) ++ not_found_return("Not building Markdown bindings.") ++endif () ++ ++# We don't need to find any libraries or anything to generate markdown ++# documentation. ++ ++# Get categories. The list of allowable categories for add_markdown_docs() is ++# in that file. ++include(MarkdownCategories.cmake) ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) ++ ++# Add sources for Markdown bindings. ++set(SOURCES ++ "binding_info.hpp" ++ "binding_info.cpp" ++ "default_param.hpp" ++ "get_binding_name.hpp" ++ "get_binding_name.cpp" ++ "get_param.hpp" ++ "get_printable_param.hpp" ++ "get_printable_param_name.hpp" ++ "get_printable_param_name_impl.hpp" ++ "get_printable_type.hpp" ++ "md_option.hpp" ++ "print_doc_functions.hpp" ++ "print_doc_functions_impl.hpp" ++ "print_docs.hpp" ++ "print_docs.cpp" ++ "print_type_doc.hpp" ++ "program_doc_wrapper.hpp" ++) ++ ++# Copy all Markdown sources to the build directory. ++add_custom_target(markdown_copy) ++add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++foreach(file ${SOURCES}) ++ add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} ARGS -E copy ++ ${CMAKE_CURRENT_SOURCE_DIR}/${file} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++endforeach() ++ ++# Create the add_markdown_docs() macro. It's meant to be used as ++# 'add_markdown_docs(knn, "cli;python;julia", "classification")', for instance. ++# See the file 'MarkdownCategories.cmake' for valid categories that can be used ++# by the macro. ++macro (add_markdown_docs name languages category) ++ ++ # First, make sure that the category is a valid category. ++ list(FIND MARKDOWN_CATEGORIES ${category} cat_index) ++ if (${cat_index} EQUAL -1) ++ string(CONCAT error_str "add_markdown_docs(): unknown category ${category}!" ++ " See the categories in " ++ "src/mlpack/bindings/markdown/MarkdownCategories.cmake.") ++ message(FATAL_ERROR "${ERROR_STR}") ++ endif () ++ ++ # Next, we should use configure_file() to generate each ++ # generate_markdown..cpp. We need to loop over all the languages for ++ # this binding to do that. ++ set(BINDING ${name}) ++ set(LANGUAGES_PUSH_BACK_CODE "") ++ set(PROGRAM_MAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${name}_main.cpp") ++ foreach (lang ${languages}) ++ set(LANGUAGES_PUSH_BACK_CODE ++ "${LANGUAGES_PUSH_BACK_CODE}\n languages.push_back(\"${lang}\");") ++ set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} ${lang}) ++ endforeach () ++ list(REMOVE_DUPLICATES MARKDOWN_ALL_LANGUAGES_LIST) ++ ++ # Do the actual file configuration. ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.hpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.hpp) ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.cpp) ++ ++ # Lastly, that generate_markdown..cpp should be added to the list of ++ # files to be compiled for the 'generate_markdown' target. We also need to ++ # add information about this binding to a set of variables we have to track. ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.hpp ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.cpp) ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} ${name}) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} ${category}) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++ set (MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) ++endmacro () ++ ++# After all the methods/ directories have been traversed, we can add the ++# 'generate_markdown' target. This function is run at the bottom of ++# methods/CMakeLists.txt. ++function (post_markdown_setup) ++ # We need to generate the program file. This consists of generating three ++ # things: ++ # ++ # - MARKDOWN_INCLUDES: the list of files to be included. ++ # - MARKDOWN_HEADER_CODE: the code used to print the header/sidebar. ++ # - MARKDOWN_CALL_CODE: the code to actually call the functions to print ++ # documentation for each binding. ++ ++ # Iterate over categories of binding. ++ list(LENGTH MARKDOWN_NAMES NUM_MARKDOWN_BINDINGS) ++ list(LENGTH MARKDOWN_CATEGORIES NUM_MARKDOWN_CATEGORIES) ++ math(EXPR cat_limit "${NUM_MARKDOWN_CATEGORIES} - 1") ++ foreach (i RANGE ${cat_limit}) ++ list (GET MARKDOWN_CATEGORIES ${i} cat) ++ # Put the things in this category in a div. ++ string(CONCAT header_code "${MARKDOWN_HEADER_CODE} cout << " ++ "\"
    \" << endl;\n " ++ "cout << \"
    ${cat}:
    \" << endl;\n") ++ set(MARKDOWN_HEADER_CODE ${header_code}) ++ ++ # Add an option for this binding. ++ math(EXPR range_limit "${NUM_MARKDOWN_BINDINGS} - 1") ++ foreach (j RANGE ${range_limit}) ++ list (GET MARKDOWN_NAME_CATEGORIES ${j} category) ++ if (NOT category STREQUAL cat) ++ continue () ++ endif () ++ ++ list (GET MARKDOWN_NAMES ${j} name) ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n Print${name}Headers();") ++ endforeach () ++ ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n cout << \"
    \" << endl << endl;") ++ endforeach () ++ ++ foreach (name ${MARKDOWN_NAMES}) ++ set (MARKDOWN_INCLUDE_CODE ++ "${MARKDOWN_INCLUDE_CODE}\n#include \"generate_markdown.${name}.hpp\"") ++ set (MARKDOWN_CALL_CODE "${MARKDOWN_CALL_CODE}\n Print${name}Docs();") ++ endforeach () ++ ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.cpp) ++ ++ # Remember that this is being run from some other directory, so we have to be ++ # explicit with the locations of the files we are compiling against. ++ add_executable(generate_markdown ++ "${BINDING_BINARY_DIR}/generate_markdown.cpp" ++ ${MARKDOWN_SRCS} ++ "${BINDING_SOURCE_DIR}/binding_info.hpp" ++ "${BINDING_SOURCE_DIR}/binding_info.cpp" ++ "${BINDING_SOURCE_DIR}/default_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.cpp" ++ "${BINDING_SOURCE_DIR}/get_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name_impl.hpp" ++ "${BINDING_SOURCE_DIR}/md_option.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions_impl.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.cpp" ++ "${BINDING_SOURCE_DIR}/program_doc_wrapper.hpp" ++ "${BINDING_SOURCE_DIR}/generate_markdown.cpp") ++ target_link_libraries(generate_markdown mlpack ${MLPACK_LIBRARIES}) ++ add_dependencies(generate_markdown markdown_copy) ++ set_target_properties(generate_markdown PROPERTIES ++ COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_MARKDOWN ++ RUNTIME_OUTPUT_DIRECTORY ${BINDING_BINARY_DIR}) ++ ++ add_custom_target(markdown ALL ++ ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/doc/ ++ COMMAND ${CMAKE_COMMAND} ++ -DPROGRAM=${BINDING_BINARY_DIR}/generate_markdown ++ -DOUTPUT_FILE=${CMAKE_BINARY_DIR}/doc/mlpack.md ++ -P ${CMAKE_SOURCE_DIR}/CMake/RunProgram.cmake ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/change_language.js ++ ${CMAKE_BINARY_DIR}/doc/res/change_languages.js ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/menu_bg.png ++ ${CMAKE_BINARY_DIR}/doc/res/menu_bg.png ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/formatting.css ++ ${CMAKE_BINARY_DIR}/doc/res/formatting.css ++ DEPENDS generate_markdown ++ COMMENT "Generating Markdown documentation for mlpack bindings...") ++endfunction () +diff --git a/src/mlpack/bindings/markdown/MarkdownCategories.cmake b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +new file mode 100644 +index 000000000..6b8d697cf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +@@ -0,0 +1,11 @@ ++# This is a list of categories of binding that Markdown can handle. If the ++# category you choose for a Markdown binding is not in this list, an error will ++# be thrown. ++set (MARKDOWN_CATEGORIES ++ "classification" ++ "regression" ++ "clustering" ++ "geometry" ++ "preprocessing" ++ "misc. / other" ++ "transformations") +diff --git a/src/mlpack/bindings/markdown/binding_info.cpp b/src/mlpack/bindings/markdown/binding_info.cpp +new file mode 100644 +index 000000000..0a61103d3 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.cpp +@@ -0,0 +1,49 @@ ++/** ++ * @file binding_info.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of BindingInfo functions. ++ */ ++#include "binding_info.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++util::ProgramDoc& BindingInfo::GetProgramDoc(const std::string& bindingName) ++{ ++ if (GetSingleton().map.count(bindingName) == 0) ++ { ++ throw std::invalid_argument("Binding name '" + bindingName + ++ "' not known!"); ++ } ++ ++ return GetSingleton().map.at(bindingName); ++} ++ ++/** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++void BindingInfo::RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc) ++{ ++ GetSingleton().map[bindingName] = programDoc; ++} ++ ++//! Get or modify the current language (don't set it to something invalid!). ++std::string& BindingInfo::Language() ++{ ++ return GetSingleton().language; ++} ++ ++BindingInfo& BindingInfo::GetSingleton() ++{ ++ static BindingInfo instance; ++ return instance; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/binding_info.hpp b/src/mlpack/bindings/markdown/binding_info.hpp +new file mode 100644 +index 000000000..6a99d2ebc +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file binding_name.hpp ++ * @author Ryan Curtin ++ * ++ * This file defines the BindingInfo singleton class that is used specifically ++ * for the Markdown bindings to map from a binding name (i.e. "knn") to ++ * multiple ProgramDoc objects, which are then used to generate the ++ * documentation. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The BindingInfo class is used by the Markdown documentation generator to ++ * store multiple ProgramDoc objects, indexed by both the binding name (i.e. ++ * "knn") and the language (i.e. "cli"). ++ */ ++class BindingInfo ++{ ++ public: ++ /** ++ * Return a ProgramDoc object for a given bindingName. ++ */ ++ static util::ProgramDoc& GetProgramDoc(const std::string& bindingName); ++ ++ /** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++ static void RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc); ++ ++ //! Get or modify the current language (don't set it to something invalid!). ++ static std::string& Language(); ++ ++ private: ++ //! Private constructor, so that only one instance can be created. ++ BindingInfo() { } ++ ++ //! Get the singleton. ++ static BindingInfo& GetSingleton(); ++ ++ //! Internally-held map for mapping a binding name to a ProgramDoc name. ++ std::unordered_map map; ++ ++ //! Holds the name of the language that we are currently printing. This is ++ //! modified before printing the documentation, and then used by ++ //! print_doc_functions.hpp during printing to print the correct language. ++ std::string language; ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/default_param.hpp b/src/mlpack/bindings/markdown/default_param.hpp +new file mode 100644 +index 000000000..78af807e0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/default_param.hpp +@@ -0,0 +1,45 @@ ++/** ++ * @file default_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get the default value of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the default value of a parameter into the output string. The type ++ * printed depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::DefaultParamImpl::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::DefaultParamImpl::type>(data); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +new file mode 100644 +index 000000000..18a07e5ec +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +@@ -0,0 +1,46 @@ ++/** ++ * @file generate_markdown.binding.cpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#define BINDING_NAME "${BINDING}" ++ ++#include ++#include "generate_markdown.${BINDING}.hpp" ++#include "binding_info.hpp" ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++static const std::string testName = "${BINDING}"; ++#include <${PROGRAM_MAIN_FILE}> ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintHeaders("${BINDING}", languages); ++} ++ ++void Print${BINDING}Docs() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintDocs("${BINDING}", languages); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +new file mode 100644 +index 000000000..3e81c9c3a +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +@@ -0,0 +1,28 @@ ++/** ++ * @file generate_markdown.binding.hpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++ ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers(); ++void Print${BINDING}Docs(); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +new file mode 100644 +index 000000000..abacae377 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +@@ -0,0 +1,114 @@ ++/** ++ * @file generate_markdown.cpp.in ++ * @author Ryan Curtin ++ * ++ * This file is configured by CMake to generate all of the Markdown required by ++ * the project. ++ */ ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++${MARKDOWN_INCLUDE_CODE} ++ ++using namespace mlpack; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++using namespace std; ++ ++int main() ++{ ++ // These come to use from CMake separated by semicolons. ++ string languageList = "${MARKDOWN_ALL_LANGUAGES_LIST}"; ++ vector languages; ++ size_t index; ++ while ((index = languageList.find(';')) != string::npos) ++ { ++ languages.push_back(languageList.substr(0, index)); ++ languageList = languageList.substr(index + 1); ++ } ++ languages.push_back(languageList); ++ ++ cout << "
    " << endl; ++ ++ // We need to create the input form selector for the language. ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << " " << endl; ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // The links to the data type sections get put here. ++ cout << " - [mlpack overview](#mlpack-overview){: .language-link #always }" ++ << endl; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << " - [data formats](#" << languages[i] << "_data-formats){: " ++ << ".language-link #" << languages[i] << " }" << endl; ++ } ++ ++ ${MARKDOWN_HEADER_CODE} ++ ++ cout << endl << "
    " << endl << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // Create all the headers for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "
    " << endl; ++ cout << "# " << util::GetVersion() << " " << PrintLanguage(languages[i]) ++ << " binding documentation" << endl; ++ cout << "
    " << endl; ++ } ++ ++ /** ++ * "mlpack overview" section. This will go at the top of the page. ++ */ ++ cout << "## mlpack overview" << endl; ++ cout << endl; ++ cout << "mlpack is an intuitive, fast, and flexible C++ machine learning " ++ "library with bindings to other languages. It is meant to be a machine " ++ "learning analog to LAPACK, and aims to implement a wide array of machine" ++ " learning methods and functions as a \"swiss army knife\" for machine " ++ "learning researchers."; ++ cout << endl << endl; ++ cout << "This reference page details mlpack's bindings to other languages. " ++ "Further useful mlpack documentation links are given below."; ++ cout << endl << endl; ++ cout << " - [mlpack homepage](https://www.mlpack.org/)" << endl; ++ cout << " - [mlpack on Github](https://github.com/mlpack/mlpack)" << endl; ++ cout << " - [mlpack main documentation page]" ++ << "(https://www.mlpack.org/docs.html)" << endl; ++ cout << endl; ++ ++ /** ++ * Discussion of different data types section. This goes just below the ++ * overview section at the top of the page. ++ */ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ cout << PrintTypeDocs() << endl; ++ } ++ ++ ${MARKDOWN_CALL_CODE} ++ cout << "
    " << endl << endl; ++ ++ // Make sure script gets included for changeLanguage(). ++ cout << "" << endl; ++} +diff --git a/src/mlpack/bindings/markdown/get_binding_name.cpp b/src/mlpack/bindings/markdown/get_binding_name.cpp +new file mode 100644 +index 000000000..67d229b6c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.cpp +@@ -0,0 +1,40 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#include "get_binding_name.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++std::string GetBindingName(const std::string& language, ++ const std::string& name) ++{ ++ // Unfortunately, every time a new binding is added, this code will need to be ++ // modified. ++ if (language == "cli") ++ { ++ // For command-line programs, all bindings have 'mlpack_' prepended to the ++ // name. ++ return "mlpack_" + name; ++ } ++ else if (language == "python") ++ { ++ // For Python bindings, the name is unchanged. ++ return name; ++ } ++ else ++ { ++ throw std::invalid_argument("Don't know how to compute binding name for " ++ "language \"" + language + "\"! Is the language specified in " ++ "src/mlpack/bindings/markdown/get_binding_name.cpp?"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/get_binding_name.hpp b/src/mlpack/bindings/markdown/get_binding_name.hpp +new file mode 100644 +index 000000000..21c55f05c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.hpp +@@ -0,0 +1,30 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given a language name and a binding name, return the name of that binding for ++ * that language. Note that if a new language is added to the mlpack bindings, ++ * this method will need to be updated so that documentation can be successfully ++ * generated for that language. ++ */ ++std::string GetBindingName(const std::string& language, ++ const std::string& name); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_param.hpp b/src/mlpack/bindings/markdown/get_param.hpp +new file mode 100644 +index 000000000..6c1611f49 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_param.hpp +@@ -0,0 +1,38 @@ ++/** ++ * @file get_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a parameter for a Markdown binding. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * All Markdown binding types are exactly what is held in the ParamData, so no ++ * special handling is necessary. ++ */ ++template ++void GetParam(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ util::ParamData& dmod = const_cast(d); ++ *((T**) output) = boost::any_cast(&dmod.value); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param.hpp b/src/mlpack/bindings/markdown/get_printable_param.hpp +new file mode 100644 +index 000000000..53a862b61 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param.hpp +@@ -0,0 +1,126 @@ ++/** ++ * @file get_printable_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a printable version of parameters. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print an option of a simple type. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a vector option, with spaces between it. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ const T& t = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ for (size_t i = 0; i < t.size(); ++i) ++ oss << t[i] << " "; ++ return oss.str(); ++} ++ ++/** ++ * Print a matrix option (this prints its size). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ // Get the matrix. ++ const T& matrix = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix"; ++ return oss.str(); ++} ++ ++/** ++ * Print a serializable class option (this prints the class name). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << data.cppType << " model at " << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a combination DatasetInfo/matrix parameter. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0) ++{ ++ // Get the matrix. ++ const T& tuple = boost::any_cast(data.value); ++ const arma::mat& matrix = std::get<1>(tuple); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix with dimension type " ++ << "information"; ++ return oss.str(); ++} ++ ++/** ++ * Print an option into a std::string. This should print a short, one-line ++ * representation of the object. The string will be stored in the output ++ * pointer. ++ * ++ * @param data Parameter data struct. ++ * @param input Unused parameter. ++ * @param output Output storage for the string. ++ */ ++template ++void GetPrintableParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParam::type>(data); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name.hpp b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +new file mode 100644 +index 000000000..9ceca6b4c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +@@ -0,0 +1,83 @@ ++/** ++ * @file get_printable_param_name.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamName( ++ const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamName::type>(d); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_name_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +new file mode 100644 +index 000000000..41b9c2972 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_param_name_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "--" + data.name; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value.hpp b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +new file mode 100644 +index 000000000..d58bc93bf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +@@ -0,0 +1,88 @@ ++/** ++ * @file get_printable_param_value.hpp ++ * @author Ryan Curtin ++ * ++ * Given a parameter value, print what the user might actually specify on the ++ * command line. Basically this adds ".csv" to types where data must be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamValue( ++ const util::ParamData& d, ++ const void* input, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamValue::type>(d, ++ *((std::string*) input)); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_value_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +new file mode 100644 +index 000000000..9fce0559b +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +@@ -0,0 +1,84 @@ ++/** ++ * @file get_printable_param_value_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter value that the user would specify on the command line ++ * depending on the type of the option. Basically this adds ".csv" to types ++ * that need to be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return input; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".csv"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".bin"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>>::type*) ++{ ++ return input + ".arff"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_type.hpp b/src/mlpack/bindings/markdown/get_printable_type.hpp +new file mode 100644 +index 000000000..2b292b937 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_type.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::GetPrintableType::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::GetPrintableType::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("GetPrintableType(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the type of a parameter. The type printed depends on the current ++ * setting of BindingInfo::Language(). ++ */ ++template ++std::string GetPrintableType(const util::ParamData& data) ++{ ++ std::string output; ++ GetPrintableType(data, (void*) NULL, (void*) &output); ++ return output; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/is_serializable.hpp b/src/mlpack/bindings/markdown/is_serializable.hpp +new file mode 100644 +index 000000000..b129487b9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/is_serializable.hpp +@@ -0,0 +1,63 @@ ++/** ++ * @file is_serializable.hpp ++ * @author Ryan Curtin ++ * ++ * Return a bool noting whether or not a parameter is serializable. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Return false, because the type is not serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::disable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return false, because even though the type is serializable, it is an ++ * Armadillo type not an mlpack model. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return true, because the type is serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0) ++{ ++ return true; ++} ++ ++/** ++ * Return whether or not the type is serializable. ++ */ ++template ++void IsSerializable(const util::ParamData& /* data */, ++ const void* /* input */, ++ void* output) ++{ ++ *((bool*) output) = IsSerializable::type>(); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/md_option.hpp b/src/mlpack/bindings/markdown/md_option.hpp +new file mode 100644 +index 000000000..dc728c4e9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/md_option.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file md_option.hpp ++ * @author Ryan Curtin ++ * ++ * The Markdown option type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++#define MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++ ++#include ++#include ++#include "default_param.hpp" ++#include "get_param.hpp" ++#include "get_printable_param.hpp" ++#include "get_printable_param_name.hpp" // For cli bindings. ++#include "get_printable_param_value.hpp" // For cli bindings. ++#include "get_printable_type.hpp" ++#include "is_serializable.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The Markdown option class. ++ */ ++template ++class MDOption ++{ ++ public: ++ /** ++ * Construct an MDOption object. When constructed, it will register itself ++ * with CLI. The testName parameter is not used and added for compatibility ++ * reasons. ++ */ ++ MDOption(const T defaultValue, ++ const std::string& identifier, ++ const std::string& description, ++ const std::string& alias, ++ const std::string& cppName, ++ const bool required = false, ++ const bool input = true, ++ const bool noTranspose = false, ++ const std::string& bindingName = "") ++ { ++ // Create the ParamData object to give to CLI. ++ util::ParamData data; ++ ++ data.desc = description; ++ data.name = identifier; ++ data.tname = TYPENAME(T); ++ data.alias = alias[0]; ++ data.wasPassed = false; ++ data.noTranspose = noTranspose; ++ data.required = required; ++ data.input = input; ++ data.loaded = false; ++ // Several options from Python and CLI bindings are persistent. ++ if (identifier == "verbose" || identifier == "copy_all_inputs" || ++ identifier == "help" || identifier == "info" || identifier == "version") ++ data.persistent = true; ++ else ++ data.persistent = false; ++ data.cppType = cppName; ++ ++ // Every parameter we'll get from Markdown will have the correct type. ++ data.value = boost::any(defaultValue); ++ ++ // Restore the parameters for this program. ++ if (identifier != "verbose" && identifier != "copy_all_inputs") ++ CLI::RestoreSettings(bindingName, false); ++ ++ // Set the function pointers that we'll need. Most of these simply delegate ++ // to the current binding type's implementation. Any new language will need ++ // to have all of these implemented, and the Markdown implementation will ++ // need to properly delegate. ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetParam"] = &GetParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = ++ &GetPrintableParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamName"] = ++ &GetPrintableParamName; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamValue"] = ++ &GetPrintableParamValue; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableType"] = ++ &GetPrintableType; ++ CLI::GetSingleton().functionMap[data.tname]["IsSerializable"] = ++ &IsSerializable; ++ ++ // Add the option. ++ CLI::Add(std::move(data)); ++ if (identifier != "verbose" && identifier != "copy_all_inputs" && ++ identifier != "help" && identifier != "info" && identifier != "version") ++ CLI::StoreSettings(bindingName); ++ CLI::ClearSettings(); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions.hpp b/src/mlpack/bindings/markdown/print_doc_functions.hpp +new file mode 100644 +index 000000000..2430fa512 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file print_doc_functions.hpp ++ * @author Ryan Curtin ++ * ++ * This file wraps the different printing functionality of different binding ++ * types. If a new binding type is added, this code will need to be modified so ++ * that Markdown can be printed. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language); ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(const std::string& language); ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs(); ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes); ++ ++/** ++ * Print the default value of an option, unless it is required (in which case ++ * Markdown italicized '--' is printed). ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset); ++ ++/** ++ * Print a model type parameter (add .bin and return). ++ */ ++inline std::string PrintModel(const std::string& model); ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args); ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName); ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d); ++ ++/** ++ * Return whether or not a runtime check on parameters should be ignored. ++ */ ++template ++inline bool IgnoreCheck(const T& t); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "print_doc_functions_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +new file mode 100644 +index 000000000..13359441d +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +@@ -0,0 +1,540 @@ ++/** ++ * @file print_doc_functions_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Call out to different printing functionality for different binding languages. ++ * If a new binding is added, this code must be modified. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++ ++#include "print_doc_functions.hpp" ++#include "binding_info.hpp" ++#include "print_type_doc.hpp" ++#include "get_printable_type.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::GetBindingName(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::GetBindingName(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language) ++{ ++ if (language == "cli") ++ { ++ return "CLI"; ++ } ++ else if (language == "python") ++ { ++ return "Python"; ++ } ++ else ++ { ++ throw std::invalid_argument("PrintLanguage(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintImport(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintImport(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintImport(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintOutputOptionInfo(); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintOutputOptionInfo(); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintOutputOptionInfo(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++namespace priv { ++ ++// We'll need a fake class for printing model type documentation. ++class mlpackModel ++{ ++ public: ++ // Fake serialization to make SFINAE work right for this type. ++ template ++ void serialize(Archive&, const unsigned int) {} ++}; ++ ++} // namespace priv ++ ++// Utility function that returns the first word (as delimited by spaces) of a ++// string. ++inline std::string ToUnderscores(const std::string& str) ++{ ++ std::string ret(str); ++ std::replace(ret.begin(), ret.end(), ' ', '_'); ++ return ret; ++} ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs() ++{ ++ std::ostringstream oss; ++ oss << "
    " << std::endl; ++ oss << "## data formats" << std::endl; ++ oss << "{: .language-types-h2 #" << BindingInfo::Language() ++ << "_data-formats }" << std::endl; ++ oss << std::endl; ++ ++ // Iterate through each of the types that we care about. ++ oss << "mlpack bindings for " << PrintLanguage(BindingInfo::Language()) ++ << " take and return a restricted set of types, for simplicity. These " ++ << "include primitive types, matrix/vector types, categorical matrix " ++ << "types, and model types. Each type is detailed below." << std::endl; ++ oss << std::endl; ++ ++ // Create fake ParamData to pass around. ++ util::ParamData data; ++ data.desc = "fake"; ++ data.name = "fake"; ++ data.tname = std::string(typeid(int).name()); ++ data.cppType = "int"; ++ data.alias = 'f'; ++ data.wasPassed = false; ++ data.noTranspose = true; ++ data.required = false; ++ data.input = true; ++ data.loaded = false; ++ data.persistent = false; ++ data.value = boost::any(int(0)); ++ ++ std::string type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(double).name()); ++ data.cppType = "double"; ++ data.value = boost::any(double(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(bool).name()); ++ data.cppType = "double"; ++ data.value = boost::any(bool(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(std::string).name()); ++ data.cppType = "std::string"; ++ data.value = boost::any(std::string("")); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: " << "#doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ ++ data.tname = std::string(typeid(arma::mat).name()); ++ data.cppType = "arma::mat"; ++ data.value = boost::any(arma::mat()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Mat).name()); ++ data.cppType = "arma::Mat"; ++ data.value = boost::any(arma::Mat()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::rowvec).name()); ++ data.cppType = "arma::rowvec"; ++ data.value = boost::any(arma::rowvec()); ++ const std::string& rowType = GetPrintableType(data); ++ ++ oss << " - `" << rowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(rowType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Row).name()); ++ data.cppType = "arma::Row"; ++ data.value = boost::any(arma::Row()); ++ const std::string& urowType = GetPrintableType>(data); ++ ++ oss << " - `" << urowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(urowType) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::vec).name()); ++ data.cppType = "arma::vec"; ++ data.value = boost::any(arma::vec()); ++ const std::string& colType = GetPrintableType(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (colType != rowType) ++ { ++ oss << " - `" << colType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(colType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ } ++ ++ data.tname = std::string(typeid(arma::Col).name()); ++ data.cppType = "arma::Col"; ++ data.value = boost::any(arma::Col()); ++ const std::string& ucolType = GetPrintableType>(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (ucolType != urowType) ++ { ++ oss << " - `" << ucolType << "`{ #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(ucolType) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ } ++ ++ data.tname = ++ std::string(typeid(std::tuple).name()); ++ data.cppType = "std::tuple"; ++ data.value = boost::any(std::tuple()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(priv::mlpackModel).name()); ++ data.cppType = "mlpackModel"; ++ data.value = boost::any(new priv::mlpackModel()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() ++ << "_model }: " << PrintTypeDoc(data) << std::endl; ++ ++ // Clean up memory. ++ delete boost::any_cast(data.value); ++ ++ oss << std::endl << "
    " << std::endl; ++ ++ return oss.str(); ++} ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintValue(value, quotes); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintValue(value, quotes); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter" + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::ostringstream oss; ++ ++ if (d.required) ++ { ++ oss << "**--**"; ++ } ++ else ++ { ++ if (BindingInfo::Language() == "cli") ++ { ++ oss << cli::PrintDefault(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ oss << python::PrintDefault(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDefault: unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ } ++ ++ return oss.str(); ++} ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintDataset(dataset); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintDataset(dataset); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDataset(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Print a model type parameter. ++ */ ++inline std::string PrintModel(const std::string& model) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintModel(model); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintModel(model); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintModel(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ s += cli::ProgramCall(programName, args...); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ s += python::ProgramCall(programName, args...); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```"; ++ return s; ++} ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += "$ " + import + "\n"; ++ s += cli::ProgramCall(programName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += ">>> " + import + "\n"; ++ s += python::ProgramCall(programName); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```\n"; ++ return s; ++} ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName) ++{ ++ // These functions always put a '' around the parameter, so we will skip that ++ // bit. ++ std::string s; ++ if (BindingInfo::Language() == "cli") ++ { ++ // The CLI bindings put a '' around the parameter, so skip that... ++ s = cli::ParamString(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s = python::ParamString(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("ParamString(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + s.substr(1, s.size() - 2) + "`"; ++} ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d) ++{ ++ std::string output; ++ CLI::GetSingleton().functionMap[d.tname]["GetPrintableType"](d, NULL, ++ &output); ++ // We want to make this a link to the type documentation. ++ std::string anchorType = output; ++ bool result; ++ CLI::GetSingleton().functionMap[d.tname]["IsSerializable"](d, NULL, &result); ++ if (result) ++ anchorType = "model"; ++ ++ return "[`" + output + "`](#doc_" + BindingInfo::Language() + "_" + ++ ToUnderscores(anchorType) + ")"; ++} ++ ++template ++inline bool IgnoreCheck(const T& t) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::IgnoreCheck(t); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::IgnoreCheck(t); ++ } ++ else ++ { ++ throw std::invalid_argument("IgnoreCheck(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_docs.cpp b/src/mlpack/bindings/markdown/print_docs.cpp +new file mode 100644 +index 000000000..2711a8789 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.cpp +@@ -0,0 +1,218 @@ ++/** ++ * @file print_docs.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of functions to print Markdown from documentation. ++ */ ++#include "print_docs.hpp" ++ ++#include ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++ ++// Make sure that this is defined. ++#ifndef DOXYGEN_PREFIX ++#define DOXYGEN_PREFIX "https://mlpack.org/docs/mlpack-git/doxygen/" ++#endif ++ ++using namespace std; ++using namespace mlpack; ++using namespace mlpack::util; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages) ++{ ++ // We just want to print the name of the function and a link, as Markdown. ++ // We have to mark it as having the right language with a div. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << " - [" << GetBindingName(bindingName) << "](#" << languages[i] ++ << "_" << bindingName << "){: .language-link #" << languages[i] << " }" ++ << endl; ++ } ++} ++ ++void PrintDocs(const std::string& bindingName, ++ const vector& languages) ++{ ++ ProgramDoc& programDoc = BindingInfo::GetProgramDoc(bindingName); ++ ++ CLI::RestoreSettings(bindingName); ++ ++ // First, for this section, print each of the names. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << "## " << GetBindingName(bindingName) << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName << " }" << endl; ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ // Next we want to print the logical name of the binding (that's known by ++ // ProgramInfo). ++ cout << "#### " << programDoc.programName << endl; ++ cout << endl; ++ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << ProgramCall(bindingName); ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ cout << programDoc.shortDocumentation << " "; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "[Detailed documentation](#" << languages[i] << "_" ++ << bindingName << "_detailed-documentation){: .language-detail-link #" ++ << languages[i] << " }"; ++ } ++ cout << "." << endl; ++ ++ // Next, print the PROGRAM_INFO() documentation for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ // This works with the Kramdown processor. ++ cout << "
    " << endl; ++ ++ // We need to print the signature. ++ ++ // Now, iterate through each of the input options. ++ cout << endl; ++ cout << "### Input options" << endl; ++ cout << endl; ++ ++ cout << "| ***name*** | ***type*** | ***description*** | ***default*** |" ++ << endl; ++ cout << "|------------|------------|-------------------|---------------|" ++ << endl; ++ map& parameters = CLI::Parameters(); ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ continue; ++ ++ // There are some special options that don't exist in some languages. ++ if (languages[i] != "python" && it->second.name == "copy_all_inputs") ++ continue; ++ if (languages[i] != "cli" && ++ (it->second.name == "help" || it->second.name == "info" || ++ it->second.name == "version")) ++ continue; ++ ++ // Print name, type, description, default. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; // just a string ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " | "; ++ string def = PrintDefault(it->second.name); ++ if (def.size() > 0) ++ cout << "`" << def << "` |"; ++ else ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ // Next, iterate through the list of output options. ++ cout << "### Output options" << endl; ++ cout << endl; ++ string outputInfo = PrintOutputOptionInfo(); ++ if (outputInfo.size() > 0) ++ cout << outputInfo << endl; ++ cout << endl; ++ cout << "| ***name*** | ***type*** | ***description*** |" << endl; ++ cout << "|------------|------------|-------------------|" << endl; ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print name, type, description. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ cout << "### Detailed documentation" << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName ++ << "_detailed-documentation }" << endl; ++ cout << endl; ++ cout << programDoc.documentation() << endl; ++ cout << endl; ++ ++ cout << "### See also" << endl; ++ cout << endl; ++ for (size_t j = 0; j < programDoc.seeAlso.size(); ++j) ++ { ++ cout << " - " << "["; ++ // We need special processing if the user has specified a binding name ++ // starting with @ (i.e., '@kfn' or similar). ++ if (programDoc.seeAlso[j].first[0] == '@') ++ cout << GetBindingName(programDoc.seeAlso[j].first.substr(1)); ++ else ++ cout << programDoc.seeAlso[j].first; ++ cout << "]("; ++ ++ // We need special handling of Doxygen information. ++ if (programDoc.seeAlso[j].second.substr(0, 8) == "@doxygen") ++ { ++ cout << DOXYGEN_PREFIX << programDoc.seeAlso[j].second.substr(9); ++ } ++ else if (programDoc.seeAlso[j].second[0] == '#') ++ { ++ cout << "#" << languages[i] << "_" ++ << programDoc.seeAlso[j].second.substr(1); ++ } ++ else ++ { ++ cout << programDoc.seeAlso[j].second; ++ } ++ ++ cout << ")" << endl; ++ } ++ cout << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ } ++ ++ CLI::ClearSettings(); ++} +diff --git a/src/mlpack/bindings/markdown/print_docs.hpp b/src/mlpack/bindings/markdown/print_docs.hpp +new file mode 100644 +index 000000000..abbc88398 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.hpp +@@ -0,0 +1,33 @@ ++/** ++ * @file print_docs.hpp ++ * @author Ryan Curtin ++ * ++ * Functions to generate Markdown for documentation of bindings. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++ ++#include ++ ++/** ++ * Given the current settings of CLI, print the header (which will be the ++ * navigation tab) for the binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ */ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages); ++ ++/** ++ * Given the current settings of CLI, print Markdown documentation for the ++ * binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ * ++ * @param bindingName The binding name (${BINDING} from CMake). ++ * @param languages The set of languages to print documentation for. ++ */ ++void PrintDocs(const std::string& bindingName, ++ const std::vector& languages); ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_type_doc.hpp b/src/mlpack/bindings/markdown/print_type_doc.hpp +new file mode 100644 +index 000000000..108461ed5 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_type_doc.hpp +@@ -0,0 +1,46 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, depending on the current language (as ++ * set in BindingInfo). ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++std::string PrintTypeDoc(const util::ParamData& data) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintTypeDoc::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintTypeDoc::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintTypeDoc(): unknown " ++ "BindingInfo::Language()" + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/program_doc_wrapper.hpp b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +new file mode 100644 +index 000000000..21da3ffc0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +@@ -0,0 +1,41 @@ ++/** ++ * @file program_doc_wrapper.hpp ++ * @author Ryan Curtin ++ * ++ * A simple wrapper around ProgramDoc that also calls ++ * BindingInfo::RegisterProgramDoc() upon construction. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++ ++#include "binding_info.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++class ProgramDocWrapper ++{ ++ public: ++ /** ++ * Construct a ProgramDoc object and register it with ++ * BindingInfo::RegisterProgramDoc(). ++ */ ++ ProgramDocWrapper(const std::string& bindingName, ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& ++ seeAlso) ++ { ++ util::ProgramDoc pd(programName, shortDocumentation, documentation, ++ seeAlso); ++ BindingInfo::RegisterProgramDoc(bindingName, pd); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/res/change_language.js b/src/mlpack/bindings/markdown/res/change_language.js +new file mode 100644 +index 000000000..4b520205f +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/change_language.js +@@ -0,0 +1,102 @@ ++/** ++ * A utility function to change the language displayed on the page. This ++ * function should be called whenever the language is changed from the ++ * drop-down. ++ */ ++function changeLanguage() ++{ ++ lang = document.getElementById("language-select").value; ++ var links = document.getElementsByClassName("language-link"); ++ for (i = 0; i < links.length; ++i) ++ { ++ // With each of the links, we get the inner
    , but we need the parent ++ //
  • . ++ if (links[i].id == lang || links[i].id == "always") ++ links[i].parentElement.style.display = "list-item"; ++ else ++ links[i].parentElement.style.display = "none"; ++ } ++ ++ var titles = document.getElementsByClassName("language-title"); ++ for (i = 0; i < titles.length; ++i) ++ { ++ if (titles[i].id == lang) ++ titles[i].style.display = "inline"; ++ else ++ titles[i].style.display = "none"; ++ } ++ ++ var headers = document.getElementsByClassName("language-header"); ++ for (i = 0; i < headers.length; ++i) ++ { ++ if (headers[i].id == lang) ++ headers[i].style.display = "inline"; ++ else ++ headers[i].style.display = "none"; ++ } ++ ++ var decls = document.getElementsByClassName("language-decl"); ++ for (i = 0; i < decls.length; ++i) ++ { ++ if (decls[i].id == lang) ++ decls[i].style.display = "inline"; ++ else ++ decls[i].style.display = "none"; ++ } ++ ++ var types = document.getElementsByClassName("language-types"); ++ for (i = 0; i < types.length; ++i) ++ { ++ if (types[i].id == lang) ++ types[i].style.display = "inline"; ++ else ++ types[i].style.display = "none"; ++ } ++ ++ var details = document.getElementsByClassName("language-detail-link"); ++ for (i = 0; i < details.length; ++i) ++ { ++ if (details[i].id == lang) ++ details[i].style.display = "inline"; ++ else ++ details[i].style.display = "none"; ++ } ++ ++ var sections = document.getElementsByClassName("language-section"); ++ for (i = 0; i < sections.length; ++i) ++ { ++ if (sections[i].id == lang) ++ sections[i].style.display = "inline"; ++ else ++ sections[i].style.display = "none"; ++ } ++} ++ ++document.body.onload = function() ++{ ++ // Do we need to manually set the language because the user came with an ++ // anchor? ++ if (window.location.hash) ++ { ++ // Try to extract the language. ++ firstUnderscore = window.location.hash.indexOf("_"); ++ if (firstUnderscore !== -1) ++ { ++ var lang = window.location.hash.substring(1, firstUnderscore); ++ // Now see if it's in the list of languages. ++ var select = document.getElementById("language-select"); ++ for (i = 0; i < select.length; ++i) ++ { ++ var select_lang = select[i].value; ++ // Is the language a match? ++ if (lang === select_lang) ++ { ++ select.value = select_lang; ++ break; ++ } ++ } ++ } ++ } ++ ++ changeLanguage(); ++} +diff --git a/src/mlpack/bindings/markdown/res/formatting.css b/src/mlpack/bindings/markdown/res/formatting.css +new file mode 100644 +index 000000000..487b026e7 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/formatting.css +@@ -0,0 +1,142 @@ ++body ++{ ++ background: #000000; ++} ++ ++/* Don't display other languages by default. */ ++div.language-title, div.language-header, div.language-decl, ++div.language-detail-link, div.language-types, div.language-section ++{ ++ display: none; ++} ++ ++/* Just display cli documentation. */ ++div.language-title#cli, div.language-header#cli, div.language-decl#cli, ++div.language-detail-link#cli, div.language-types#cli, div.language-section#cli ++{ ++ display: none; ++} ++ ++div#header ++{ ++ padding-top: 10px; ++ width: 200px; ++ background: #000000; ++ border-right: 2px solid #333333; ++ height: 100%; ++ position: fixed; ++ z-index: 1; ++ top: 0; ++ left: 0; ++ overflow-y: scroll; ++} ++ ++div#header .language-select-div select ++{ ++ color: #ffffff; ++ background: transparent; ++ border: none; ++ height: 29px; ++ padding: 5px; ++ width: 190px; ++} ++ ++div#header .language-select-div ++{ ++ color: #ffffff; ++ border: 2px solid #333333; ++ background: url('../res/menu_bg.png') no-repeat 96% 0; ++ height: 34px; ++ width: 180px; ++ overflow: hidden; ++ margin-bottom: 20px; ++ ++ -webkit-border-radius: 5px; ++ -moz-border-radius: 5px; ++ border-radius: 5px; ++} ++ ++div#header ul ++{ ++ padding-top: 5px; ++ margin-bottom: 5px; ++ color: #bb2000; ++ display: table; ++ text-align: left; ++ padding-left: 0px; ++ margin-left: 15px; ++ font-size: 75%; ++} ++ ++div#header ul li ++{ ++ line-height: 1.3em; ++} ++ ++div#header ul li a ++{ ++ color: #eab72c; ++ font-weight: none; ++} ++ ++div#header ul li a:hover ++{ ++ color: #ffffff; ++ font-weight: none; ++} ++ ++div#docs ++{ ++ margin-left: 200px; ++} ++ ++span.special ++{ ++ font-size: 85%; ++ color: #eab72c; ++} ++ ++div.category ++{ ++ text-align: left; ++} ++ ++div.category h5 ++{ ++ text-align: left; ++ margin-top: 0; ++ margin-bottom: 0; ++ color: #ffffff; ++ font-size: 100%; ++ font-weight: normal; ++ padding: 0; ++} ++ ++h2#mlpack-overview, h2.language-types-h2 ++{ ++ padding-bottom: 0.75em; ++} ++ ++div.language-header h1 ++{ ++ color: #ffffff; ++ text-align: center; ++ border-top: none; ++ border-bottom: 1px solid #eab72c; ++} ++ ++div.language-types li code ++{ ++ font-weight: bold; ++ color: #eab72c; ++} ++ ++div.language-types li ++{ ++ padding-bottom: 1em; ++} ++ ++div.language-section table td a code:hover ++{ ++ color: #bb2000; ++} +diff --git a/src/mlpack/bindings/markdown/res/menu_bg.png b/src/mlpack/bindings/markdown/res/menu_bg.png +new file mode 100644 +index 0000000000000000000000000000000000000000..c29580a400267755c0f2a7d1b8f154d329797553 +GIT binary patch +literal 395 +zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I +zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@$z$e7@|Ns9$rm%z} +zkO5&YUc4A6vSP&wAh~+=Y9P6B<3=C}Q4qtiwFt=JD+%%oW?-oBD6-sRv)}%J<6##S +z{uAOr8O9`UcNcz%T?{vY9QG1VUsv|Wj9gsIB1StofUaP$^K@|xk+_^3kmk^E@KBlv +z3nQb?wiDJ01q~ZqKi}J1EyfTU{PA6^wa?xsyTXMZR(+r2Hbc2uaP~}(9I0R241Yd| +zZn0k0^$2K^YKdz^NlIc#s#S7PDv)9@GBC8%H89jQGzc*?wK6caGBVIL05S}G9 ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. This is for regular types. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a matrix option, a tuple option, a ++ * serializable option, or a string option (this returns the default filename, ++ * or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */ = 0); ++ ++/** ++ * Return the default value of a model option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of an option. This is the function that will be ++ * placed into the CLI functionMap. ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ std::string* outstr = (std::string*) output; ++ *outstr = DefaultParamImpl::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "default_param_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/default_param_impl.hpp b/src/mlpack/bindings/python/default_param_impl.hpp +new file mode 100644 +index 000000000..bae57cce2 +--- /dev/null ++++ b/src/mlpack/bindings/python/default_param_impl.hpp +@@ -0,0 +1,147 @@ ++/** ++ * @file default_param_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the default value of a parameter, depending on its type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++ ++#include "default_param.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type* /* junk */) ++{ ++ std::ostringstream oss; ++ if (std::is_same::value) ++ oss << "False"; ++ else ++ oss << boost::any_cast(data.value); ++ ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ // Print each element in an array delimited by square brackets. ++ std::ostringstream oss; ++ const T& vector = boost::any_cast(data.value); ++ oss << "["; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ const std::string& s = *boost::any_cast(&data.value); ++ return "'" + s + "'"; ++} ++ ++/** ++ * Return the default value of a matrix option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */) ++{ ++ // Get the filename and return it, or return an empty string. ++ if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "np.empty([0])"; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "np.empty([0], dtype=np.uint64)"; ++ } ++ else if (std::is_same>::value) ++ { ++ return "np.empty([0, 0], dtype=np.uint64)"; ++ } ++ else ++ { ++ return "np.empty([0, 0])"; ++ } ++} ++ ++/** ++ * Return the default value of a model option (always "None"). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ return "None"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_cython_type.hpp b/src/mlpack/bindings/python/get_cython_type.hpp +index e44f4f70b..e44b91eb0 100644 +--- a/src/mlpack/bindings/python/get_cython_type.hpp ++++ b/src/mlpack/bindings/python/get_cython_type.hpp +@@ -40,16 +40,6 @@ inline std::string GetCythonType( + return "int"; + } + +-template<> +-inline std::string GetCythonType( +- const util::ParamData& /* d */, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*) +-{ +- return "float"; +-} +- + template<> + inline std::string GetCythonType( + const util::ParamData& /* d */, +diff --git a/src/mlpack/bindings/python/get_printable_type.hpp b/src/mlpack/bindings/python/get_printable_type.hpp +new file mode 100644 +index 000000000..51a528b4d +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type.hpp +@@ -0,0 +1,120 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++void GetPrintableType(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(d); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_printable_type_impl.hpp b/src/mlpack/bindings/python/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..2031b3435 +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type_impl.hpp +@@ -0,0 +1,151 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "unknown"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "int"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "double"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "string"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "size_t"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "bool"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "list of " + GetPrintableType(d) + "s"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ std::string type = "matrix"; ++ if (std::is_same::value) ++ { ++ if (T::is_row || T::is_col) ++ type = "vector"; ++ } ++ else if (std::is_same::value) ++ { ++ type = "int matrix"; ++ if (T::is_row || T::is_col) ++ type = "int vector"; ++ } ++ ++ return type; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type*) ++{ ++ return "categorical matrix"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return d.cppType + "Type"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_doc.hpp b/src/mlpack/bindings/python/print_doc.hpp +index 7a5a27781..b8962ccaf 100644 +--- a/src/mlpack/bindings/python/print_doc.hpp ++++ b/src/mlpack/bindings/python/print_doc.hpp +@@ -14,7 +14,7 @@ + + #include + #include +-#include "get_python_type.hpp" ++#include "get_printable_type.hpp" + + namespace mlpack { + namespace bindings { +@@ -44,24 +44,20 @@ void PrintDoc(const util::ParamData& d, + oss << d.name << "_ ("; + else + oss << d.name << " ("; +- oss << GetPythonType::type>(d) << "): " ++ oss << GetPrintableType::type>(d) << "): " + << d.desc; + + // Print a default, if possible. + if (!d.required) + { +- if (d.cppType == "std::string") ++ // Call the correct overload to get the default value directly. ++ if (d.cppType == "std::string" || d.cppType == "double" || ++ d.cppType == "int" || d.cppType == "std::vector" || ++ d.cppType == "std::vector" || ++ d.cppType == "std::vector") + { +- oss << " Default value '" << boost::any_cast(d.value) +- << "'."; +- } +- else if (d.cppType == "double") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; +- } +- else if (d.cppType == "int") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; ++ std::string defaultValue = DefaultParamImpl(d); ++ oss << " Default value " << defaultValue << "."; + } + } + +diff --git a/src/mlpack/bindings/python/print_doc_functions.hpp b/src/mlpack/bindings/python/print_doc_functions.hpp +index a9207c570..51e85b78d 100644 +--- a/src/mlpack/bindings/python/print_doc_functions.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions.hpp +@@ -19,6 +19,21 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -29,6 +44,11 @@ inline std::string PrintValue(const T& value, bool quotes); + template<> + inline std::string PrintValue(const bool& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + // Recursion base case. + inline std::string PrintInputOptions(); + +@@ -57,6 +77,12 @@ std::string PrintOutputOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +diff --git a/src/mlpack/bindings/python/print_doc_functions_impl.hpp b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +index c27e88609..0de6a5312 100644 +--- a/src/mlpack/bindings/python/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +@@ -19,6 +19,32 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ // No modification is needed to the name---we just use it as-is. ++ return bindingName + "()"; ++} ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ return "from mlpack import " + bindingName; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return "Results are returned in a Python dictionary. The keys of the " ++ "dictionary are the names of the output parameters."; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -48,6 +74,23 @@ inline std::string PrintValue(const bool& value, bool quotes) + return "False"; + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + // Recursion base case. + std::string PrintInputOptions() { return ""; } + +@@ -136,7 +179,8 @@ std::string PrintOutputOptions(const std::string& paramName, + + /** + * Given a name of a binding and a variable number of arguments (and their +- * contents), print the corresponding function call. ++ * contents), print the corresponding function call. The given programName ++ * should not be the output of GetBindingName(). + */ + template + std::string ProgramCall(const std::string& programName, Args... args) +@@ -166,6 +210,76 @@ std::string ProgramCall(const std::string& programName, Args... args) + return util::HyphenateString(call, 2) + "\n" + oss.str(); + } + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. The programName should not be the output of GetBindingName(). ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << ">>> "; ++ ++ // Determine if we have any output options. ++ const std::map& parameters = CLI::Parameters(); ++ bool hasOutput = false; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ { ++ hasOutput = true; ++ break; ++ } ++ } ++ ++ if (hasOutput) ++ oss << "d = "; ++ ++ oss << programName << "("; ++ ++ // Now iterate over every input option. ++ bool first = true; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ if (!first) ++ oss << ", "; ++ else ++ first = false; ++ ++ // Print the input option. ++ if (it->second.name != "lambda") // Don't print Python keywords. ++ oss << it->second.name << "="; ++ else ++ oss << it->second.name << "_="; ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ oss << value; ++ } ++ oss << ")"; ++ ++ std::string result = util::HyphenateString(oss.str(), 8); ++ ++ oss.str(""); ++ oss << result; ++ ++ // Now print output lines. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print a new line for the output option. ++ oss << std::endl << ">>> " << it->second.name << " = d['" ++ << it->second.name << "']"; ++ } ++ ++ return oss.str(); ++} ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +@@ -183,14 +297,6 @@ inline std::string PrintDataset(const std::string& datasetName) + return "'" + datasetName + "'"; + } + +-/** +- * Given the name of a binding, print its invocation. +- */ +-inline std::string ProgramCall(const std::string& programName) +-{ +- return ">>> " + programName + "("; +-} +- + /** + * Print any closing call to a program. For a Python binding this is a closing + * brace. +diff --git a/src/mlpack/bindings/python/print_input_processing.hpp b/src/mlpack/bindings/python/print_input_processing.hpp +index e271906cb..04f318a7c 100644 +--- a/src/mlpack/bindings/python/print_input_processing.hpp ++++ b/src/mlpack/bindings/python/print_input_processing.hpp +@@ -17,7 +17,6 @@ + #include "get_numpy_type.hpp" + #include "get_numpy_type_char.hpp" + #include "get_cython_type.hpp" +-#include "get_python_type.hpp" + #include "strip_type.hpp" + + namespace mlpack { +diff --git a/src/mlpack/bindings/python/print_type_doc.hpp b/src/mlpack/bindings/python/print_type_doc.hpp +new file mode 100644 +index 000000000..9a5d6dc71 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_type_doc_impl.hpp b/src/mlpack/bindings/python/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..8d6ae3971 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc_impl.hpp +@@ -0,0 +1,162 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option (True or False)."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A list of integers; i.e., [0, 1, 2]."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A list of strings; i.e., [\"hello\", \"goodbye\"]."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data. This can be a 2-d matrix where " ++ "one dimension has size 1, or it can also be a list, a numpy 1-d " ++ "ndarray, or a 1-d pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data. This can be a list of lists, a " ++ "numpy ndarray, or a pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ } ++ else if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data with a uint64 dtype. This can be" ++ " a 2-d matrix where one dimension has size 1, or it can also be a " ++ "list, a numpy 1-d ndarray, or a 1-d pandas DataFrame. If the dtype " ++ "is not already uint64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data with a uint64 dtype. This can " ++ "be a list of lists, a numpy ndarray, or a pandas DataFrame. If the " ++ "dtype is not already uint64, it will be converted."; ++ } ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A 2-d arraylike containing data. Like the regular 2-d matrices, this" ++ " can be a list of lists, a numpy ndarray, or a pandas DataFrame. " ++ "However, this type can also accept a pandas DataFrame that has columns " ++ "of type 'CategoricalDtype'. These categorical values will be converted " ++ "to numeric indices before being passed to mlpack, and then inside mlpack" ++ " they will be properly treated as categorical variables, so there is no " ++ "need to do one-hot encoding for this matrix type. If the dtype of the " ++ "given matrix is not already float64, it will be converted."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "An mlpack model pointer. This type can be pickled to or from disk, " ++ "and internally holds a pointer to C++ memory containing the mlpack " ++ "model. Note that this means that the mlpack model itself cannot be " ++ "easily inspected in Python; however, the pickled model can be loaded " ++ "in C++ and inspected there."; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/py_option.hpp b/src/mlpack/bindings/python/py_option.hpp +index 98bba2dd5..e175d282b 100644 +--- a/src/mlpack/bindings/python/py_option.hpp ++++ b/src/mlpack/bindings/python/py_option.hpp +@@ -13,6 +13,7 @@ + #define MLPACK_BINDINGS_PYTHON_PY_OPTION_HPP + + #include ++#include "default_param.hpp" + #include "get_param.hpp" + #include "get_printable_param.hpp" + #include "print_class_defn.hpp" +@@ -85,6 +86,9 @@ class PyOption + CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = + &GetPrintableParam; + ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ + // These are used by the pyx generator. + CLI::GetSingleton().functionMap[data.tname]["PrintClassDefn"] = + &PrintClassDefn; +diff --git a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +index cf3c1055d..93daaddc4 100644 +--- a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp ++++ b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +@@ -19,6 +19,7 @@ using namespace mlpack; + using namespace mlpack::kernel; + + PROGRAM_INFO("Python binding test", ++ "A simple program to test Python binding functionality.", + "A simple program to test Python binding functionality. You can build " + "mlpack with the BUILD_TESTS option set to off, and this binding will " + "no longer be built."); +diff --git a/src/mlpack/core/util/cli.cpp b/src/mlpack/core/util/cli.cpp +index f2bf6cec2..1a83080bd 100644 +--- a/src/mlpack/core/util/cli.cpp ++++ b/src/mlpack/core/util/cli.cpp +@@ -27,7 +27,8 @@ using namespace mlpack; + using namespace mlpack::util; + + // Fake ProgramDoc in case none is supplied. +-static ProgramDoc emptyProgramDoc = ProgramDoc("", []() { return ""; }); ++static ProgramDoc emptyProgramDoc = ProgramDoc("", "", []() { return ""; }, ++ {}); + + /* Constructors, Destructors, Copy */ + /* Make the constructor private, to preclude unauthorized instances */ +diff --git a/src/mlpack/core/util/mlpack_main.hpp b/src/mlpack/core/util/mlpack_main.hpp +index 4c915c0c0..8003f38ac 100644 +--- a/src/mlpack/core/util/mlpack_main.hpp ++++ b/src/mlpack/core/util/mlpack_main.hpp +@@ -21,6 +21,7 @@ + #define BINDING_TYPE_CLI 0 + #define BINDING_TYPE_TEST 1 + #define BINDING_TYPE_PYX 2 ++#define BINDING_TYPE_MARKDOWN 128 + #define BINDING_TYPE_UNKNOWN -1 + + #ifndef BINDING_TYPE +@@ -99,9 +100,10 @@ using Option = mlpack::bindings::tests::TestOption; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }) + + #elif(BINDING_TYPE == BINDING_TYPE_PYX) // This is a Python binding. + +@@ -128,9 +130,10 @@ static const std::string testName = ""; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); \ ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }); \ + namespace mlpack { \ + namespace bindings { \ + namespace python { \ +@@ -148,6 +151,59 @@ PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" + + // Nothing else needs to be defined---the binding will use mlpackMain() as-is. + ++#elif BINDING_TYPE == BINDING_TYPE_MARKDOWN ++ ++// It doesn't really matter whether this is true or false... ++#define BINDING_MATRIX_TRANSPOSED true ++ ++// We use BINDING_NAME in PROGRAM_INFO() so it needs to be defined. ++#ifndef BINDING_NAME ++ #error "BINDING_NAME must be defined when BINDING_TYPE is Markdown!" ++#endif ++ ++#include ++#include ++ ++#define PRINT_PARAM_STRING mlpack::bindings::markdown::ParamString ++#define PRINT_PARAM_VALUE mlpack::bindings::markdown::PrintValue ++#define PRINT_DATASET mlpack::bindings::markdown::PrintDataset ++#define PRINT_MODEL mlpack::bindings::markdown::PrintModel ++#define PRINT_CALL mlpack::bindings::markdown::ProgramCall ++#define BINDING_IGNORE_CHECK mlpack::bindings::markdown::IgnoreCheck ++ ++namespace mlpack { ++namespace util { ++ ++template ++using Option = mlpack::bindings::markdown::MDOption; ++ ++} ++} ++ ++#include ++#include ++ ++#undef PROGRAM_INFO ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) static \ ++ mlpack::bindings::markdown::ProgramDocWrapper \ ++ cli_programdoc_dummy_object = \ ++ mlpack::bindings::markdown::ProgramDocWrapper(BINDING_NAME, NAME, \ ++ SHORT_DESC, []() { return DESC; }, { __VA_ARGS__ }); \ ++ ++PARAM_FLAG("verbose", "Display informational messages and the full list of " ++ "parameters and timers at the end of execution.", "v"); ++ ++// CLI-specific parameters. ++PARAM_FLAG("help", "Default help info.", "h"); ++PARAM_STRING_IN("info", "Print help on a specific option.", "", ""); ++PARAM_FLAG("version", "Display the version of mlpack.", "V"); ++ ++// Python-specific parameters. ++PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" ++ " copied before the method is run. This is useful for debugging problems " ++ "where the input parameters are being modified by the algorithm, but can " ++ "slow down the code.", ""); ++ + #else + + #error "Unknown binding type! Be sure BINDING_TYPE is defined if you are " \ +diff --git a/src/mlpack/core/util/param.hpp b/src/mlpack/core/util/param.hpp +index b812887fb..de50f5b00 100644 +--- a/src/mlpack/core/util/param.hpp ++++ b/src/mlpack/core/util/param.hpp +@@ -29,6 +29,21 @@ using DatasetInfo = DatasetMapper; + } // namespace data + } // namespace mlpack + ++/** ++ * Provide a link for a binding's "see also" documentation section, which is ++ * primarily (but not necessarily exclusively) used by the Markdown bindings ++ * This link can be specified by calling SEE_ALSO("description", "link"), where ++ * "description" is the description of the link and "link" may be one of the ++ * following: ++ * ++ * - A direct URL, starting with http:// or https://. ++ * - A page anchor for documentation, referencing another binding by its CMake ++ * binding name, i.e. "#knn". ++ * - A link to a Doxygen page, using the mangled Doxygen name after a ++ * '@doxygen/', i.e., "@doxygen/mlpack1_1_adaboost1_1_AdaBoost". ++ */ ++#define SEE_ALSO(DESCRIPTION, LINK) {DESCRIPTION, LINK} ++ + /** + * Document an executable. Only one instance of this macro should be + * present in your program! Therefore, use it in the main.cpp +@@ -41,14 +56,22 @@ using DatasetInfo = DatasetMapper; + * PARAM_DOUBLE_OUT_REQ(), PARAM_VECTOR_OUT_REQ(), PARAM_STRING_OUT_REQ(). + * + * @param NAME Short string representing the name of the program. ++ * @param SHORT_DESC Short two-sentence description of the program; it should ++ * describe what the program implements and does, and a quick overview of ++ * how it can be used and what it should be used for. + * @param DESC Long string describing what the program does and possibly a + * simple usage example. Newlines should not be used here; this is taken + * care of by CLI (however, you can explicitly specify newlines to denote +- * new paragraphs). ++ * new paragraphs). You can also use printing macros like ++ * PRINT_PARAM_STRING(), PRINT_DATASET(), and others. ++ * @param SEE_ALSOS A set of SEE_ALSO() macros that are used for generating ++ * documentation. See the SEE_ALSO() macro. This is a varargs argument, so ++ * you can add as many SEE_ALSO()s as you like. + */ +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }) ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ } ) + + /** + * Define a flag parameter. +diff --git a/src/mlpack/core/util/program_doc.cpp b/src/mlpack/core/util/program_doc.cpp +index 814573b1c..28d7ee8b8 100644 +--- a/src/mlpack/core/util/program_doc.cpp ++++ b/src/mlpack/core/util/program_doc.cpp +@@ -23,17 +23,33 @@ using namespace std; + * Construct a ProgramDoc object. When constructed, it will register itself + * with CLI. A fatal error will be thrown if more than one is constructed. + * +- * @param programName Short string representing the name of the program. +- * @param documentation Long string containing documentation on how to use the +- * program and what it is. No newline characters are necessary; this is +- * taken care of by CLI later. + * @param defaultModule Name of the default module. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. ++ * @param documentation Long string containing documentation on how to use the ++ * program and what it is. No newline characters are necessary; this is ++ * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ +-ProgramDoc::ProgramDoc(const std::string& programName, +- const std::function& documentation) : ++ProgramDoc::ProgramDoc( ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso) : + programName(programName), +- documentation(documentation) ++ shortDocumentation(shortDocumentation), ++ documentation(documentation), ++ seeAlso(seeAlso) + { + // Register this with CLI. + CLI::RegisterProgramDoc(this); + } ++ ++/** ++ * Construct an empty ProgramDoc object. ++ */ ++ProgramDoc::ProgramDoc() ++{ ++ CLI::RegisterProgramDoc(this); ++} +diff --git a/src/mlpack/core/util/program_doc.hpp b/src/mlpack/core/util/program_doc.hpp +index f5de89fe5..296b15586 100644 +--- a/src/mlpack/core/util/program_doc.hpp ++++ b/src/mlpack/core/util/program_doc.hpp +@@ -33,17 +33,32 @@ class ProgramDoc + * will be returned. + * + * @param programName Short string representing the name of the program. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. + * @param documentation Long string containing documentation on how to use the + * program and what it is. No newline characters are necessary; this is + * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ + ProgramDoc(const std::string& programName, +- const std::function& documentation); ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso); ++ ++ /** ++ * Construct an empty ProgramDoc object. (This is not meant to be used!) ++ */ ++ ProgramDoc(); + + //! The name of the program. + std::string programName; ++ //! The short documentation for the program. ++ std::string shortDocumentation; + //! Documentation for what the program does. + std::function documentation; ++ //! Set of see also information. ++ std::vector> seeAlso; + }; + + } // namespace util +diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt +index 4e6fc3df9..360e79f93 100644 +--- a/src/mlpack/methods/CMakeLists.txt ++++ b/src/mlpack/methods/CMakeLists.txt +@@ -74,3 +74,7 @@ endforeach() + + set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) ++set(MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++set(MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++set(MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) +diff --git a/src/mlpack/methods/adaboost/CMakeLists.txt b/src/mlpack/methods/adaboost/CMakeLists.txt +index 5505f057f..3e85237e0 100644 +--- a/src/mlpack/methods/adaboost/CMakeLists.txt ++++ b/src/mlpack/methods/adaboost/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(adaboost) + add_python_binding(adaboost) ++add_markdown_docs(adaboost "cli;python" "classification") +diff --git a/src/mlpack/methods/adaboost/adaboost_main.cpp b/src/mlpack/methods/adaboost/adaboost_main.cpp +index 0c3be12e2..ced3d3c1f 100644 +--- a/src/mlpack/methods/adaboost/adaboost_main.cpp ++++ b/src/mlpack/methods/adaboost/adaboost_main.cpp +@@ -46,7 +46,14 @@ using namespace mlpack::decision_stump; + using namespace mlpack::perceptron; + using namespace mlpack::util; + +-PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " ++PROGRAM_INFO("AdaBoost", ++ // Short description. ++ "An implementation of the AdaBoost.MH (Adaptive Boosting) algorithm for " ++ "classification. This can be used to train an AdaBoost model on labeled " ++ "data or use an existing AdaBoost model to predict the classes of new " ++ "points.", ++ // Long description. ++ "This program implements the AdaBoost (or Adaptive " + "Boosting) algorithm. The variant of AdaBoost implemented here is " + "AdaBoost.MH. It uses a weak learner, either decision stumps or " + "perceptrons, and over many iterations, creates a strong learner that is a " +@@ -72,7 +79,7 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + "classes for each point in the test dataset are output to the " + + PRINT_PARAM_STRING("output") + " output parameter. The AdaBoost model " + "itself is output to the " + PRINT_PARAM_STRING("output_model") + +- "output parameter." ++ " output parameter." + "\n\n" + "For example, to run AdaBoost on an input dataset " + + PRINT_DATASET("data") + " with perceptrons as the weak learner type, " +@@ -88,7 +95,15 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + PRINT_DATASET("predictions") + " with the following command: " + "\n\n" + + PRINT_CALL("adaboost", "input_model", "model", "test", "test_data", +- "output", "predictions")); ++ "output", "predictions"), ++ // See also... ++ SEE_ALSO("AdaBoost on Wikipedia", "https://en.wikipedia.org/wiki/AdaBoost"), ++ SEE_ALSO("Improved boosting algorithms using confidence-rated predictions " ++ "(pdf)", "http://rob.schapire.net/papers/SchapireSi98.pdf"), ++ SEE_ALSO("Perceptron", "#perceptron"), ++ SEE_ALSO("Decision Stump", "#decision_stump"), ++ SEE_ALSO("mlpack::adaboost::AdaBoost C++ class documentation", ++ "@doxygen/classmlpack_1_1adaboost_1_1AdaBoost.html")); + + // Input for training. + PARAM_MATRIX_IN("training", "Dataset for training AdaBoost.", "t"); +diff --git a/src/mlpack/methods/approx_kfn/CMakeLists.txt b/src/mlpack/methods/approx_kfn/CMakeLists.txt +index da20af02b..be1a5c4f0 100644 +--- a/src/mlpack/methods/approx_kfn/CMakeLists.txt ++++ b/src/mlpack/methods/approx_kfn/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # This program computes approximate furthest neighbors. + add_cli_executable(approx_kfn) + add_python_binding(approx_kfn) ++add_markdown_docs(approx_kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +index 9adce050e..7091536fd 100644 +--- a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp ++++ b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +@@ -22,6 +22,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Approximate furthest neighbor search", ++ // Short description. ++ "An implementation of two strategies for furthest neighbor search. This " ++ "can be used to compute the furthest neighbor of query point(s) from a set " ++ "of points; furthest neighbor models can be saved and reused with future " ++ "query point(s).", ++ // Long description. + "This program implements two strategies for furthest neighbor search. " + "These strategies are:" + "\n\n" +@@ -86,7 +92,18 @@ PROGRAM_INFO("Approximate furthest neighbor search", + PRINT_DATASET("neighbors") + " by calling" + "\n\n" + + PRINT_CALL("approx_kfn", "input_model", "model", "query", "new_query_set", +- "k", 3, "neighbors", "neighbors")); ++ "k", 3, "neighbors", "neighbors"), ++ SEE_ALSO("k-furthest-neighbor search", "#kfn"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Fast approximate furthest neighbors with data-dependent candidate" ++ " selection (pdf)", "http://ratml.org/pub/pdf/2016fast.pdf"), ++ SEE_ALSO("Approximate furthest neighbor in high dimensions (pdf)", ++ "https://pdfs.semanticscholar.org/a4b5/7b9cbf37201fb1d9a56c0f4eefad0466" ++ "9c20.pdf"), ++ SEE_ALSO("mlpack::neighbor::QDAFN class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1QDAFN.html"), ++ SEE_ALSO("mlpack::neighbor::DrusillaSelect class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1DrusillaSelect.html")); + + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); + PARAM_MATRIX_IN("query", "Matrix containing query points.", "q"); +diff --git a/src/mlpack/methods/cf/CMakeLists.txt b/src/mlpack/methods/cf/CMakeLists.txt +index 01796f29d..976304458 100644 +--- a/src/mlpack/methods/cf/CMakeLists.txt ++++ b/src/mlpack/methods/cf/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(cf) + add_python_binding(cf) ++add_markdown_docs(cf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp +index 237fc7267..743e9007c 100644 +--- a/src/mlpack/methods/cf/cf_main.cpp ++++ b/src/mlpack/methods/cf/cf_main.cpp +@@ -30,7 +30,13 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " ++PROGRAM_INFO("Collaborative Filtering", ++ // Short description. ++ "An implementation of several collaborative filtering (CF) techniques for " ++ "recommender systems. This can be used to train a new CF model, or use an" ++ " existing CF model to compute recommendations.", ++ // Long description. ++ "This program performs collaborative " + "filtering (CF) on the given dataset. Given a list of user, item and " + "preferences (the " + PRINT_PARAM_STRING("training") + " parameter), " + "the program will perform a matrix decomposition and then can perform a " +@@ -53,7 +59,7 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "addition, the number of recommendations per user to generate can be " + "specified with the " + PRINT_PARAM_STRING("recommendations") + " " + "parameter, and the number of similar users (the size of the neighborhood) " +- " to be considered when generating recommendations can be specified with " ++ "to be considered when generating recommendations can be specified with " + "the " + PRINT_PARAM_STRING("neighborhood") + " parameter." + "\n\n" + "For performing the matrix decomposition, the following optimization " +@@ -83,7 +89,20 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "call " + "\n\n" + + PRINT_CALL("cf", "input_model", "model", "query", "users", +- "recommendations", 5, "output", "recommendations")); ++ "recommendations", 5, "output", "recommendations"), ++ SEE_ALSO("Collaborative filtering tutorial", "@doxygen/cftutorial.html"), ++ SEE_ALSO("Alternating Matrix Factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Collaborative Filtering on Wikipedia", ++ "https://en.wikipedia.org/wiki/Collaborative_filtering"), ++ SEE_ALSO("Matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Matrix_factorization_" ++ "(recommender_systems)"), ++ SEE_ALSO("Matrix factorization techniques for recommender systems (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.441.3234" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::cf::CFType class documentation", ++ "@doxygen/classmlpack_1_1cf_1_1CFType.html")); + + // Parameters for training a model. + PARAM_MATRIX_IN("training", "Input dataset to perform CF on.", "t"); +diff --git a/src/mlpack/methods/dbscan/CMakeLists.txt b/src/mlpack/methods/dbscan/CMakeLists.txt +index 70939c9d0..47b79d782 100644 +--- a/src/mlpack/methods/dbscan/CMakeLists.txt ++++ b/src/mlpack/methods/dbscan/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(dbscan) + add_python_binding(dbscan) ++add_markdown_docs(dbscan "cli;python" "clustering") +diff --git a/src/mlpack/methods/dbscan/dbscan_main.cpp b/src/mlpack/methods/dbscan/dbscan_main.cpp +index c4afc3c85..04bcfc85f 100644 +--- a/src/mlpack/methods/dbscan/dbscan_main.cpp ++++ b/src/mlpack/methods/dbscan/dbscan_main.cpp +@@ -27,6 +27,10 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("DBSCAN clustering", ++ // Short description. ++ "An implementation of DBSCAN clustering. Given a dataset, this can " ++ "compute and return a clustering of that dataset.", ++ // Long description. + "This program implements the DBSCAN algorithm for clustering using " + "accelerated tree-based range search. The type of tree that is used " + "may be parameterized, or brute-force range search may also be used." +@@ -59,7 +63,13 @@ PROGRAM_INFO("DBSCAN clustering", + PRINT_DATASET("input") + " with a radius of 0.5 and a minimum cluster size" + " of 5 is given below:" + "\n\n" + +- PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5)); ++ PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5), ++ SEE_ALSO("DBSCAN on Wikipedia", "https://en.wikipedia.org/wiki/DBSCAN"), ++ SEE_ALSO("A density-based algorithm for discovering clusters in large " ++ "spatial databases with noise (pdf)", ++ "http://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf"), ++ SEE_ALSO("mlpack::dbscan::DBSCAN class documentation", ++ "@doxygen/classmlpack_1_1dbscan_1_1DBSCAN.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to cluster.", "i"); + PARAM_UROW_OUT("assignments", "Output matrix for assignments of each " +diff --git a/src/mlpack/methods/decision_stump/CMakeLists.txt b/src/mlpack/methods/decision_stump/CMakeLists.txt +index e9d9c04c1..a11b0048e 100644 +--- a/src/mlpack/methods/decision_stump/CMakeLists.txt ++++ b/src/mlpack/methods/decision_stump/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_stump) + add_python_binding(decision_stump) ++add_markdown_docs(decision_stump "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_stump/decision_stump_main.cpp b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +index aa4d9a67d..6f8f308af 100644 +--- a/src/mlpack/methods/decision_stump/decision_stump_main.cpp ++++ b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +@@ -22,6 +22,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Decision Stump", ++ // Short description. ++ "An implementation of a decision stump, which is a single-level decision " ++ "tree. Given labeled data, a new decision stump can be trained; or, an " ++ "existing decision stump can be used to classify points.", ++ // Long description. + "This program implements a decision stump, which is a single-level decision" + " tree. The decision stump will split on one dimension of the input data, " + "and will split into multiple buckets. The dimension and bins are selected" +@@ -61,7 +66,12 @@ PROGRAM_INFO("Decision Stump", + "\n\n" + "After training, a decision stump can be saved with the " + + PRINT_PARAM_STRING("output_model") + " output parameter. That stump may " +- "later be re-used in subsequent calls to this program (or others)."); ++ "later be re-used in subsequent calls to this program (or others).", ++ SEE_ALSO("Decision tree", "#decision_tree"), ++ SEE_ALSO("Decision stumps on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_stump"), ++ SEE_ALSO("mlpack::decision_stump::DecisionStump class documentation", ++ "@doxygen/classmlpack_1_1decision__stump_1_1DecisionStump.html")); + + // Datasets we might load. + PARAM_MATRIX_IN("training", "The dataset to train on.", "t"); +diff --git a/src/mlpack/methods/decision_tree/CMakeLists.txt b/src/mlpack/methods/decision_tree/CMakeLists.txt +index 448e4d62e..3f46f0d95 100644 +--- a/src/mlpack/methods/decision_tree/CMakeLists.txt ++++ b/src/mlpack/methods/decision_tree/CMakeLists.txt +@@ -27,3 +27,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_tree) + add_python_binding(decision_tree) ++add_markdown_docs(decision_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_tree/decision_tree_main.cpp b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +index 0e4be96c0..312b8e962 100644 +--- a/src/mlpack/methods/decision_tree/decision_tree_main.cpp ++++ b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +@@ -21,6 +21,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Decision tree", ++ // Short description. ++ "An implementation of an ID3-style decision tree for classification, which" ++ " supports categorical data. Given labeled data with numeric or " ++ "categorical features, a decision tree can be trained and saved; or, an " ++ "existing decision tree can be used for classification on new points.", ++ // Long description. + "Train and evaluate using a decision tree. Given a dataset containing " + "numeric or categorical features, and associated labels for each point in " + "the dataset, this program can train a decision tree on that data." +@@ -70,7 +76,15 @@ PROGRAM_INFO("Decision tree", + PRINT_DATASET("predictions") + ", one could call " + "\n\n" + + PRINT_CALL("decision_tree", "input_model", "tree", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("Decision stump", "#decision_stump"), ++ SEE_ALSO("Random forest", "#random_forest"), ++ SEE_ALSO("Decision trees on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_tree_learning"), ++ SEE_ALSO("Induction of Decision Trees (pdf)", ++ "https://link.springer.com/content/pdf/10.1007/BF00116251.pdf"), ++ SEE_ALSO("mlpack::tree::DecisionTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1DecisionTree.html")); + + // Datasets. + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", +diff --git a/src/mlpack/methods/det/CMakeLists.txt b/src/mlpack/methods/det/CMakeLists.txt +index 762c7b11c..58b402c7c 100644 +--- a/src/mlpack/methods/det/CMakeLists.txt ++++ b/src/mlpack/methods/det/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(det) + add_python_binding(det) ++add_markdown_docs(det "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp +index 135baa703..df2db8169 100644 +--- a/src/mlpack/methods/det/det_main.cpp ++++ b/src/mlpack/methods/det/det_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Density Estimation With Density Estimation Trees", ++ // Short description. ++ "An implementation of density estimation trees for the density estimation " ++ "task. Density estimation trees can be trained or used to predict the " ++ "density at locations given by query points.", ++ // Long description. + "This program performs a number of functions related to Density Estimation " + "Trees. The optimal Density Estimation Tree (DET) can be trained on a set " + "of data (specified by " + PRINT_PARAM_STRING("training") + ") using " +@@ -49,7 +54,15 @@ PROGRAM_INFO("Density Estimation With Density Estimation Trees", + "trained on the given training points, or a tree given as the parameter " + + PRINT_PARAM_STRING("input_model") + ". The density estimates for the test" + " points may be saved using the " + +- PRINT_PARAM_STRING("test_set_estimates") + " output parameter."); ++ PRINT_PARAM_STRING("test_set_estimates") + " output parameter.", ++ SEE_ALSO("Density estimation tree (DET) tutorial", ++ "@doxygen/dettutorial.html"), ++ SEE_ALSO("Density estimation on Wikipedia", ++ "https://en.wikipedia.org/wiki/Density_estimation"), ++ SEE_ALSO("Density estimation trees (pdf)", ++ "http://www.mlpack.org/papers/det.pdf"), ++ SEE_ALSO("mlpack::tree::DTree class documentation", ++ "@doxygen/classmlpack_1_1det_1_1DTree.html")); + + // Input data files. + PARAM_MATRIX_IN("training", "The data set on which to build a density " +diff --git a/src/mlpack/methods/emst/CMakeLists.txt b/src/mlpack/methods/emst/CMakeLists.txt +index 73cd431cb..7a073a8f9 100644 +--- a/src/mlpack/methods/emst/CMakeLists.txt ++++ b/src/mlpack/methods/emst/CMakeLists.txt +@@ -23,3 +23,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(emst) + add_python_binding(emst) ++add_markdown_docs(emst "cli;python" "geometry") +diff --git a/src/mlpack/methods/emst/emst_main.cpp b/src/mlpack/methods/emst/emst_main.cpp +index 3b0f9de98..e60ba7b64 100644 +--- a/src/mlpack/methods/emst/emst_main.cpp ++++ b/src/mlpack/methods/emst/emst_main.cpp +@@ -31,6 +31,10 @@ + #include "dtb.hpp" + + PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", ++ // Short description. ++ "An implementation of the Dual-Tree Boruvka algorithm for computing the " ++ "Euclidean minimum spanning tree of a set of input points.", ++ // Long description. + "This program can compute the Euclidean minimum spanning tree of a set of " + "input points using the dual-tree Boruvka algorithm." + "\n\n" +@@ -56,7 +60,14 @@ PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", + "The output matrix is a three-dimensional matrix, where each row indicates " + "an edge. The first dimension corresponds to the lesser index of the edge;" + " the second dimension corresponds to the greater index of the edge; and " +- "the third column corresponds to the distance between the two points."); ++ "the third column corresponds to the distance between the two points.", ++ SEE_ALSO("EMST Tutorial", "@doxygen/emst_tutorial.html"), ++ SEE_ALSO("Minimum spanning tree on Wikipedia", ++ "https://en.wikipedia.org/wiki/Minimum_spanning_tree"), ++ SEE_ALSO("Fast Euclidean Minimum Spanning Tree: Algorithm, Analysis, and " ++ "Applications (pdf)", "http://www.mlpack.org/papers/emst.pdf"), ++ SEE_ALSO("mlpack::emst::DualTreeBoruvka class documentation", ++ "@doxygen/classmlpack_1_1emst_1_1DualTreeBoruvka.html")); + + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); + PARAM_MATRIX_OUT("output", "Output data. Stored as an edge list.", "o"); +diff --git a/src/mlpack/methods/fastmks/CMakeLists.txt b/src/mlpack/methods/fastmks/CMakeLists.txt +index d9a6a6a8e..e6b6e55c1 100644 +--- a/src/mlpack/methods/fastmks/CMakeLists.txt ++++ b/src/mlpack/methods/fastmks/CMakeLists.txt +@@ -22,3 +22,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(fastmks) + add_python_binding(fastmks) ++add_markdown_docs(fastmks "cli;python" "geometry") +diff --git a/src/mlpack/methods/fastmks/fastmks_main.cpp b/src/mlpack/methods/fastmks/fastmks_main.cpp +index 1d8e7e7fb..fea273d65 100644 +--- a/src/mlpack/methods/fastmks/fastmks_main.cpp ++++ b/src/mlpack/methods/fastmks/fastmks_main.cpp +@@ -25,6 +25,12 @@ using namespace mlpack::metric; + using namespace mlpack::util; + + PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", ++ // Short description. ++ "An implementation of the single-tree and dual-tree fast max-kernel search" ++ " (FastMKS) algorithm. Given a set of reference points and a set of query" ++ " points, this can find the reference point with maximum kernel value for " ++ "each query point; trained models can be reused for future queries.", ++ // Long description. + "This program will find the k maximum kernels of a set of points, " + "using a query set and a reference set (which can optionally be the same " + "set). More specifically, for each point in the query set, the k points in" +@@ -51,7 +57,14 @@ PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", + "\n\n" + "This program performs FastMKS using a cover tree. The base used to build " + "the cover tree can be specified with the " + PRINT_PARAM_STRING("base") + +- " parameter."); ++ " parameter.", ++ SEE_ALSO("Fast max-kernel search tutorial (fastmks)", ++ "@doxygen/fmkstutorial.html"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Dual-tree Fast Exact Max-Kernel Search (pdf)", ++ "http://mlpack.org/papers/fmks.pdf"), ++ SEE_ALSO("mlpack::fastmks::FastMKS class documentation", ++ "@doxygen/classmlpack_1_1fastmks_1_1FastMKS.html")); + + // Model-building parameters. + PARAM_MATRIX_IN("reference", "The reference dataset.", "r"); +diff --git a/src/mlpack/methods/gmm/CMakeLists.txt b/src/mlpack/methods/gmm/CMakeLists.txt +index 74f9e8ace..964f490be 100644 +--- a/src/mlpack/methods/gmm/CMakeLists.txt ++++ b/src/mlpack/methods/gmm/CMakeLists.txt +@@ -23,7 +23,12 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(gmm_train) + add_python_binding(gmm_train) ++add_markdown_docs(gmm_train "cli;python" "clustering") ++ + add_cli_executable(gmm_generate) + add_python_binding(gmm_generate) ++add_markdown_docs(gmm_generate "cli;python" "clustering") ++ + add_cli_executable(gmm_probability) + add_python_binding(gmm_probability) ++add_markdown_docs(gmm_probability "cli;python" "clustering") +diff --git a/src/mlpack/methods/gmm/gmm_generate_main.cpp b/src/mlpack/methods/gmm/gmm_generate_main.cpp +index 784234941..4f3d10fdb 100644 +--- a/src/mlpack/methods/gmm/gmm_generate_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_generate_main.cpp +@@ -20,6 +20,10 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Sample Generator", ++ // Short description. ++ "A sample generator for pre-trained GMMs. Given a pre-trained GMM, this " ++ "can sample new points randomly from that distribution.", ++ // Long description. + "This program is able to generate samples from a pre-trained GMM (use " + "gmm_train to train a GMM). The pre-trained GMM must be specified with " + "the " + PRINT_PARAM_STRING("input_model") + " parameter. The number " +@@ -32,7 +36,13 @@ PROGRAM_INFO("GMM Sample Generator", + "samples in " + PRINT_DATASET("samples") + ":" + "\n\n" + + PRINT_CALL("gmm_generate", "input_model", "gmm", "samples", 100, "output", +- "samples")); ++ "samples"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM model to generate samples " + "from.", "m"); +diff --git a/src/mlpack/methods/gmm/gmm_probability_main.cpp b/src/mlpack/methods/gmm/gmm_probability_main.cpp +index f40c86628..6ba29585c 100644 +--- a/src/mlpack/methods/gmm/gmm_probability_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_probability_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Probability Calculator", ++ // Short description. ++ "A probability calculator for GMMs. Given a pre-trained GMM and a set of " ++ "points, this can compute the probability that each point is from the given" ++ " GMM.", ++ // Long description. + "This program calculates the probability that given points came from a " + "given GMM (that is, P(X | gmm)). The GMM is specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and the points are " +@@ -33,7 +38,13 @@ PROGRAM_INFO("GMM Probability Calculator", + PRINT_DATASET("probs") + ", the following command could be used:" + "\n\n" + + PRINT_CALL("gmm_probability", "input_model", "gmm", "input", "points", +- "output", "probs")); ++ "output", "probs"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM to use as model.", "m"); + PARAM_MATRIX_IN_REQ("input", "Input matrix to calculate probabilities of.", +diff --git a/src/mlpack/methods/gmm/gmm_train_main.cpp b/src/mlpack/methods/gmm/gmm_train_main.cpp +index c331d04dc..a01adfcb0 100644 +--- a/src/mlpack/methods/gmm/gmm_train_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_train_main.cpp +@@ -26,6 +26,11 @@ using namespace mlpack::kmeans; + using namespace std; + + PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", ++ // Short description. ++ "An implementation of the EM algorithm for training Gaussian mixture " ++ "models (GMMs). Given a dataset, this can train a GMM for future use " ++ "with other tools.", ++ // Long description. + "This program takes a parametric estimate of a Gaussian mixture model (GMM)" + " using the EM algorithm to find the maximum likelihood estimate. The " + "model may be saved and reused by other mlpack GMM tools." +@@ -84,7 +89,13 @@ PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", + ", the following command may be used: " + "\n\n" + + PRINT_CALL("gmm_train", "input_model", "gmm", "input", "data2", +- "gaussians", 6, "output_model", "new_gmm")); ++ "gaussians", 6, "output_model", "new_gmm"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + // Parameters for training. + PARAM_MATRIX_IN_REQ("input", "The training data on which the model will be " +diff --git a/src/mlpack/methods/hmm/CMakeLists.txt b/src/mlpack/methods/hmm/CMakeLists.txt +index 7add2baa3..7247430bb 100644 +--- a/src/mlpack/methods/hmm/CMakeLists.txt ++++ b/src/mlpack/methods/hmm/CMakeLists.txt +@@ -21,9 +21,16 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hmm_train) + add_python_binding(hmm_train) ++add_markdown_docs(hmm_train "cli;python" "misc. / other") ++ + add_cli_executable(hmm_loglik) + add_python_binding(hmm_loglik) ++add_markdown_docs(hmm_loglik "cli;python" "misc. / other") ++ + add_cli_executable(hmm_viterbi) + add_python_binding(hmm_viterbi) ++add_markdown_docs(hmm_viterbi "cli;python" "misc. / other") ++ + add_cli_executable(hmm_generate) + add_python_binding(hmm_generate) ++add_markdown_docs(hmm_generate "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/hmm/hmm_generate_main.cpp b/src/mlpack/methods/hmm/hmm_generate_main.cpp +index 0efc8dc84..cbfbe30bf 100644 +--- a/src/mlpack/methods/hmm/hmm_generate_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_generate_main.cpp +@@ -29,8 +29,13 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " +- "utility takes an already-trained HMM, specified as the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", ++ // Short description. ++ "A utility to generate random sequences from a pre-trained Hidden Markov " ++ "Model (HMM). The length of the desired sequence can be specified, and a " ++ "random sequence of observations is returned.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as the " + + PRINT_PARAM_STRING("model") + " parameter, and generates a random " + "observation sequence and hidden state sequence based on its parameters. " + "The observation sequence may be saved with the " + +@@ -47,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " + PRINT_DATASET("states") + ", the following command may be used: " + "\n\n" + + PRINT_CALL("hmm_generate", "model", "hmm", "length", 150, "output", +- "observations", "state", "states")); ++ "observations", "state", "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MODEL_IN_REQ(HMMModel, "model", "Trained HMM to generate sequences with.", + "m"); +diff --git a/src/mlpack/methods/hmm/hmm_loglik_main.cpp b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +index 6ebb3cbef..23a482603 100644 +--- a/src/mlpack/methods/hmm/hmm_loglik_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +@@ -26,8 +26,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " +- "utility takes an already-trained HMM, specified with the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", ++ // Short description. ++ "A utility for computing the log-likelihood of a sequence for Hidden Markov" ++ " Models (HMMs). Given a pre-trained HMM and an observation sequence, this" ++ " computes and returns the log-likelihood of that sequence being observed " ++ "from that HMM.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and evaluates the " + "log-likelihood of a sequence of observations, given with the " + + PRINT_PARAM_STRING("input") + " parameter. The computed log-likelihood is" +@@ -37,7 +43,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " + PRINT_DATASET("seq") + " with the pre-trained HMM " + PRINT_MODEL("hmm") + + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm")); ++ PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "File containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "File containing HMM.", "m"); +diff --git a/src/mlpack/methods/hmm/hmm_train_main.cpp b/src/mlpack/methods/hmm/hmm_train_main.cpp +index 109cfbddd..3e21085a1 100644 +--- a/src/mlpack/methods/hmm/hmm_train_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_train_main.cpp +@@ -27,9 +27,15 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " +- "Hidden Markov Model to be trained on labeled or unlabeled data. It " +- "support three types of HMMs: discrete HMMs, Gaussian HMMs, or GMM HMMs." ++PROGRAM_INFO("Hidden Markov Model (HMM) Training", ++ // Short description. ++ "An implementation of training algorithms for Hidden Markov Models (HMMs). " ++ "Given labeled or unlabeled data, an HMM can be trained for further use " ++ "with other mlpack HMM tools.", ++ // Long description. ++ "This program allows a Hidden Markov Model to be trained on labeled or " ++ "unlabeled data. It supports three types of HMMs: discrete HMMs, " ++ "Gaussian HMMs, or GMM HMMs." + "\n\n" + "Either one input sequence can be specified (with --input_file), or, a " + "file containing files in which input sequences can be found (when " +@@ -46,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " + "\n\n" + "Optionally, a pre-created HMM model can be used as a guess for the " + "transition matrix and emission probabilities; this is specifiable with " +- "--model_file."); ++ "--model_file.", ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_STRING_IN_REQ("input_file", "File containing input observations.", "i"); + PARAM_STRING_IN("type", "Type of HMM: discrete | gaussian | gmm.", "t", +diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +index d0e1168b3..1fa3c5f58 100644 +--- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +@@ -27,8 +27,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " +- "utility takes an already-trained HMM, specified as " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", ++ // Short description. ++ "A utility for computing the most probable hidden state sequence for Hidden" ++ " Markov Models (HMMs). Given a pre-trained HMM and an observed sequence, " ++ "this uses the Viterbi algorithm to compute and return the most probable " ++ "hidden state sequence.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as " + + PRINT_PARAM_STRING("input_model") + ", and evaluates the most probable " + "hidden state sequence of a given sequence of observations (specified as " + "'" + PRINT_PARAM_STRING("input") + ", using the Viterbi algorithm. The " +@@ -41,7 +47,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " + ", the following command could be used:" + "\n\n" + + PRINT_CALL("hmm_viterbi", "input", "obs", "input_model", "hmm", "output", +- "states")); ++ "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "Matrix containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "Trained HMM to use.", "m"); +diff --git a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +index b573a19c6..2a94415b3 100644 +--- a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt ++++ b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +@@ -30,3 +30,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hoeffding_tree) + add_python_binding(hoeffding_tree) ++add_markdown_docs(hoeffding_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +index 46437b4ad..45b58713c 100644 +--- a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp ++++ b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +@@ -26,6 +26,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Hoeffding trees", ++ // Short description. ++ "An implementation of Hoeffding trees, a form of streaming decision tree " ++ "for classification. Given labeled data, a Hoeffding tree can be trained " ++ "and saved for later use, or a pre-trained Hoeffding tree can be used for " ++ "predicting the classifications of new points.", ++ // Long description. + "This program implements Hoeffding trees, a form of streaming decision tree" + " suited best for large (or streaming) datasets. This program supports " + "both categorical and numeric data. Given an input dataset, this program " +@@ -70,7 +76,13 @@ PROGRAM_INFO("Hoeffding trees", + PRINT_DATASET("class_probs") + " with the following command: " + "\n\n" + + PRINT_CALL("hoeffding_tree", "input_model", "tree", "test", "test_set", +- "predictions", "predictions", "probabilities", "class_probs")); ++ "predictions", "predictions", "probabilities", "class_probs"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Mining High-Speed Data Streams (pdf)", ++ "http://dm.cs.washington.edu/papers/vfdt-kdd00.pdf"), ++ SEE_ALSO("mlpack::tree::HoeffdingTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1HoeffdingTree.html")); + + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", + "t"); +diff --git a/src/mlpack/methods/kernel_pca/CMakeLists.txt b/src/mlpack/methods/kernel_pca/CMakeLists.txt +index c14a9f3ec..9bd0cd3a0 100644 +--- a/src/mlpack/methods/kernel_pca/CMakeLists.txt ++++ b/src/mlpack/methods/kernel_pca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kernel_pca) + add_python_binding(kernel_pca) ++add_markdown_docs(kernel_pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +index a9b3478e0..a736c27ed 100644 +--- a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp ++++ b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +@@ -41,6 +41,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Kernel Principal Components Analysis", ++ // Short description. ++ "An implementation of Kernel Principal Components Analysis (KPCA). This " ++ "can be used to perform nonlinear dimensionality reduction or preprocessing" ++ " on a given dataset.", ++ // Long description. + "This program performs Kernel Principal Components Analysis (KPCA) on the " + "specified dataset with the specified kernel. This will transform the " + "data onto the kernel principal components, and optionally reduce the " +@@ -93,7 +98,13 @@ PROGRAM_INFO("Kernel Principal Components Analysis", + "the kernel matrix; to specify the sampling scheme, the " + + PRINT_PARAM_STRING("sampling") + " parameter is used. The " + "sampling scheme for the Nystr\u00F6m method can be chosen from the " +- "following list: 'kmeans', 'random', 'ordered'."); ++ "following list: 'kmeans', 'random', 'ordered'.", ++ SEE_ALSO("Kernel principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Kernel_principal_component_analysis"), ++ SEE_ALSO("Kernel Principal Component Analysis (pdf)", ++ "http://pca.narod.ru/scholkopf_kernel.pdf"), ++ SEE_ALSO("mlpack::kpca::KernelPCA class documentation", ++ "@doxygen/classmlpack_1_1kpca_1_1KernelPCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform KPCA on.", "i"); + PARAM_MATRIX_OUT("output", "Matrix to save modified dataset to.", "o"); +@@ -107,9 +118,9 @@ PARAM_INT_IN("new_dimensionality", "If not 0, reduce the dimensionality of " + PARAM_FLAG("center", "If set, the transformed data will be centered about the " + "origin.", "c"); + +-PARAM_FLAG("nystroem_method", "If set, the nystroem method will be used.", "n"); ++PARAM_FLAG("nystroem_method", "If set, the Nystroem method will be used.", "n"); + +-PARAM_STRING_IN("sampling", "Sampling scheme to use for the nystroem method: " ++PARAM_STRING_IN("sampling", "Sampling scheme to use for the Nystroem method: " + "'kmeans', 'random', 'ordered'", "s", "kmeans"); + + PARAM_DOUBLE_IN("kernel_scale", "Scale, for 'hyptan' kernel.", "S", 1.0); +diff --git a/src/mlpack/methods/kmeans/CMakeLists.txt b/src/mlpack/methods/kmeans/CMakeLists.txt +index bf4990083..9cfec0fdc 100644 +--- a/src/mlpack/methods/kmeans/CMakeLists.txt ++++ b/src/mlpack/methods/kmeans/CMakeLists.txt +@@ -40,3 +40,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kmeans) + add_python_binding(kmeans) ++add_markdown_docs(kmeans "cli;python" "clustering") +diff --git a/src/mlpack/methods/kmeans/kmeans_main.cpp b/src/mlpack/methods/kmeans/kmeans_main.cpp +index b0319ccb5..9d3f40c7d 100644 +--- a/src/mlpack/methods/kmeans/kmeans_main.cpp ++++ b/src/mlpack/methods/kmeans/kmeans_main.cpp +@@ -28,11 +28,17 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " +- "on the given dataset. It can return the learned cluster assignments, and " +- "the centroids of the clusters. Empty clusters are not allowed by default;" +- " when a cluster becomes empty, the point furthest from the centroid of the" +- " cluster with maximum variance is taken to fill that cluster." ++PROGRAM_INFO("K-Means Clustering", ++ // Short description. ++ "An implementation of several strategies for efficient k-means clustering. " ++ "Given a dataset and a value of k, this computes and returns a k-means " ++ "clustering on that data.", ++ // Long description. ++ "This program performs K-Means clustering on the given dataset. It can " ++ "return the learned cluster assignments, and the centroids of the clusters." ++ " Empty clusters are not allowed by default; when a cluster becomes empty," ++ " the point furthest from the centroid of the cluster with maximum variance" ++ " is taken to fill that cluster." + "\n\n" + "Optionally, the Bradley and Fayyad approach (\"Refining initial points for" + " k-means clustering\", 1998) can be used to select initial points by " +@@ -85,7 +91,21 @@ PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " + "following command may be used:" + "\n\n" + + PRINT_CALL("kmeans", "input", "data", "initial_centroids", "initial", +- "clusters", 10, "max_iterations", 500, "centroid", "final")); ++ "clusters", 10, "max_iterations", 500, "centroid", "final"), ++ SEE_ALSO("K-Means tutorial", "@doxygen/kmtutorial.html"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Using the triangle inequality to accelerate k-means (pdf)", ++ "http://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf"), ++ SEE_ALSO("Making k-means even faster (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.586.2554" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("Accelerating exact k-means algorithms with geometric reasoning " ++ "(pdf)", "http://reports-archive.adm.cs.cmu.edu/anon/anon/usr/ftp/" ++ "usr0/ftp/2000/CMU-CS-00-105.pdf"), ++ SEE_ALSO("A dual-tree algorithm for fast k-means clustering with large k " ++ "(pdf)", "http://www.ratml.org/pub/pdf/2017dual.pdf"), ++ SEE_ALSO("mlpack::kmeans::KMeans class documentation", ++ "@doxygen/classmlpack_1_1kmeans_1_1KMeans.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/lars/CMakeLists.txt b/src/mlpack/methods/lars/CMakeLists.txt +index 1388f24be..7e4cb7890 100644 +--- a/src/mlpack/methods/lars/CMakeLists.txt ++++ b/src/mlpack/methods/lars/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(lars) + add_python_binding(lars) ++add_markdown_docs(lars "cli;python" "regression") +diff --git a/src/mlpack/methods/lars/lars_main.cpp b/src/mlpack/methods/lars/lars_main.cpp +index 040c5f0c3..e0b2f38f3 100644 +--- a/src/mlpack/methods/lars/lars_main.cpp ++++ b/src/mlpack/methods/lars/lars_main.cpp +@@ -21,10 +21,16 @@ using namespace mlpack; + using namespace mlpack::regression; + using namespace mlpack::util; + +-PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " +- "(Stagewise/laSso). This is a stage-wise homotopy-based algorithm for " +- "L1-regularized linear regression (LASSO) and L1+L2-regularized linear " +- "regression (Elastic Net)." ++PROGRAM_INFO("LARS", ++ // Short description. ++ "An implementation of Least Angle Regression (Stagewise/laSso), also known" ++ " as LARS. This can train a LARS/LASSO/Elastic Net model and use that " ++ "model or a pre-trained model to output regression predictions for a test " ++ "set.", ++ // Long description. ++ "An implementation of LARS: Least Angle Regression (Stagewise/laSso). " ++ "This is a stage-wise homotopy-based algorithm for L1-regularized linear " ++ "regression (LASSO) and L1+L2-regularized linear regression (Elastic Net)." + "\n\n" + "This program is able to train a LARS/LASSO/Elastic Net model or load a " + "model from file, output regression predictions for a test set, and save " +@@ -80,7 +86,12 @@ PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " + "and save those responses to " + PRINT_DATASET("test_predictions") + ": " + "\n\n" + + PRINT_CALL("lars", "input_model", "lasso_model", "test", "test", +- "output_predictions", "test_predictions")); ++ "output_predictions", "test_predictions"), ++ SEE_ALSO("@linear_regression", "#linear_regression"), ++ SEE_ALSO("Least angle regression (pdf)", ++ "http://mlpack.org/papers/lars.pdf"), ++ SEE_ALSO("mlpack::regression::LARS C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LARS.html")); + + PARAM_TMATRIX_IN("input", "Matrix of covariates (X).", "i"); + PARAM_MATRIX_IN("responses", "Matrix of responses/observations (y).", "r"); +diff --git a/src/mlpack/methods/linear_regression/CMakeLists.txt b/src/mlpack/methods/linear_regression/CMakeLists.txt +index 58a7426e8..b4e63cdca 100644 +--- a/src/mlpack/methods/linear_regression/CMakeLists.txt ++++ b/src/mlpack/methods/linear_regression/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(linear_regression) + add_python_binding(linear_regression) ++add_markdown_docs(linear_regression "cli;python" "regression") +diff --git a/src/mlpack/methods/linear_regression/linear_regression_main.cpp b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +index c1e33cd06..1cb58a85c 100644 +--- a/src/mlpack/methods/linear_regression/linear_regression_main.cpp ++++ b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +@@ -22,6 +22,12 @@ using namespace arma; + using namespace std; + + PROGRAM_INFO("Simple Linear Regression and Prediction", ++ // Short description. ++ "An implementation of simple linear regression and ridge regression using " ++ "ordinary least squares. Given a dataset and responses, a model can be " ++ "trained and saved for later use, or a pre-trained model can be used to " ++ "output regression predictions for a test set.", ++ // Long description. + "An implementation of simple linear regression and simple ridge regression " + "using ordinary least squares. This solves the problem" + "\n\n" +@@ -63,7 +69,13 @@ PROGRAM_INFO("Simple Linear Regression and Prediction", + "used:" + "\n\n" + + PRINT_CALL("linear_regression", "input_model", "lr_model", "test", "X_test", +- "output_predictions", "X_test_responses")); ++ "output_predictions", "X_test_responses"), ++ SEE_ALSO("Linear/ridge regression tutorial", "@doxygen/lrtutorial.html"), ++ SEE_ALSO("@lars", "#lars"), ++ SEE_ALSO("Linear regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Linear_regression"), ++ SEE_ALSO("mlpack::regression::LinearRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LinearRegression.html")); + + PARAM_MATRIX_IN("training", "Matrix containing training set X (regressors).", + "t"); +diff --git a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +index 6720525c7..d4b498e55 100644 +--- a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt ++++ b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(local_coordinate_coding) + add_python_binding(local_coordinate_coding) ++add_markdown_docs(local_coordinate_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +index e0b8c2964..0925382bc 100644 +--- a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp ++++ b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +@@ -24,6 +24,12 @@ using namespace mlpack::sparse_coding; // For NothingInitializer. + using namespace mlpack::util; + + PROGRAM_INFO("Local Coordinate Coding", ++ // Short description. ++ "An implementation of Local Coordinate Coding (LCC), a data transformation " ++ "technique. Given input data, this transforms each point to be expressed " ++ "as a linear combination of a few points in the dataset; once an LCC model " ++ "is trained, it can be used to transform points later also.", ++ // Long description. + "An implementation of Local Coordinate Coding (LCC), which " + "codes data that approximately lives on a manifold using a variation of l1-" + "norm regularized sparse coding. Given a dense data matrix X with n points" +@@ -67,7 +73,13 @@ PROGRAM_INFO("Local Coordinate Coding", + "be used:" + "\n\n" + + PRINT_CALL("local_coordinate_coding", "input_model", "lcc_model", "test", +- "points", "codes", "new_codes")); ++ "points", "codes", "new_codes"), ++ SEE_ALSO("@sparse_coding", "#sparse_coding"), ++ SEE_ALSO("Nonlinear learning using local coordinate coding (pdf)", ++ "https://papers.nips.cc/paper/3875-nonlinear-learning-using-local-" ++ "coordinate-coding.pdf"), ++ SEE_ALSO("mlpack::lcc::LocalCoordinateCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1lcc_1_1LocalCoordinateCoding.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +diff --git a/src/mlpack/methods/logistic_regression/CMakeLists.txt b/src/mlpack/methods/logistic_regression/CMakeLists.txt +index d7edd6ba1..673e214c6 100644 +--- a/src/mlpack/methods/logistic_regression/CMakeLists.txt ++++ b/src/mlpack/methods/logistic_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(logistic_regression) + add_python_binding(logistic_regression) ++add_markdown_docs(logistic_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +index 2a0ed23a7..385525ab0 100644 +--- a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp ++++ b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +@@ -24,6 +24,11 @@ using namespace mlpack::optimization; + using namespace mlpack::util; + + PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", ++ // Short description. ++ "An implementation of L2-regularized logistic regression for two-class " ++ "classification. Given labeled data, a model can be trained and saved for " ++ "future use; or, a pre-trained model can be used to classify new points.", ++ // Long description. + "An implementation of L2-regularized logistic regression using either the " + "L-BFGS optimizer or SGD (stochastic gradient descent). This solves the " + "regression problem" +@@ -39,8 +44,8 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + "those things at once. In addition, this program allows classification on " + "a test dataset (specified with the " + PRINT_PARAM_STRING("test") + " " + "parameter) and the classification results may be saved with the " + +- PRINT_PARAM_STRING("output") + " output parameter. The trained logistic " +- "regression model may be saved using the " + ++ PRINT_PARAM_STRING("output") + " output parameter." ++ " The trained logistic regression model may be saved using the " + + PRINT_PARAM_STRING("output_model") + " output parameter." + "\n\n" + "The training data, if specified, may have class labels as its last " +@@ -95,7 +100,13 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + PRINT_DATASET("predictions") + "', the following command may be used: " + "\n\n" + + PRINT_CALL("logistic_regression", "input_model", "lr_model", "test", "test", +- "output", "predictions")); ++ "output", "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Logistic regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Logistic_regression"), ++ SEE_ALSO("mlpack::regression::LogisticRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LogisticRegression.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/lsh/CMakeLists.txt b/src/mlpack/methods/lsh/CMakeLists.txt +index 758a2152b..4ec7baacf 100644 +--- a/src/mlpack/methods/lsh/CMakeLists.txt ++++ b/src/mlpack/methods/lsh/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # sets with p-stable LSH. + add_cli_executable(lsh) + add_python_binding(lsh) ++add_markdown_docs(lsh "cli;python" "geometry") +diff --git a/src/mlpack/methods/lsh/lsh_main.cpp b/src/mlpack/methods/lsh/lsh_main.cpp +index d9923e02b..c082e10aa 100644 +--- a/src/mlpack/methods/lsh/lsh_main.cpp ++++ b/src/mlpack/methods/lsh/lsh_main.cpp +@@ -25,6 +25,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", ++ // Short description. ++ "An implementation of approximate k-nearest-neighbor search with " ++ "locality-sensitive hashing (LSH). Given a set of reference points and a " ++ "set of query points, this will compute the k approximate nearest neighbors" ++ " of each query point in the reference set; models can be saved for future " ++ "use.", ++ // Long description. + "This program will calculate the k approximate-nearest-neighbors of a set " + "of points using locality-sensitive hashing. You may specify a separate set" + " of reference points and query points, or just a reference set which will " +@@ -49,7 +56,15 @@ PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", + " parameter can be specified to set the random seed." + "\n\n" + "This program also has many other parameters to control its functionality;" +- " see the parameter-specific documentation for more information."); ++ " see the parameter-specific documentation for more information.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("Locality-sensitive hashing on Wikipedia", ++ "https://en.wikipedia.org/wiki/Locality-sensitive_hashing"), ++ SEE_ALSO("Locality-sensitive hashing scheme based on p-stable distributions" ++ " (pdf)", "http://mlpack.org/papers/lsh.pdf"), ++ SEE_ALSO("mlpack::neighbor::LSHSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1LSHSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/mean_shift/CMakeLists.txt b/src/mlpack/methods/mean_shift/CMakeLists.txt +index 1087ab532..5aced5225 100644 +--- a/src/mlpack/methods/mean_shift/CMakeLists.txt ++++ b/src/mlpack/methods/mean_shift/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(mean_shift) + add_python_binding(mean_shift) ++add_markdown_docs(mean_shift "cli;python" "clustering") +diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +index 4710ccaad..ac7f17e18 100644 +--- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp ++++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +@@ -23,9 +23,15 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " +- "clustering on the given dataset, storing the learned cluster assignments " +- "either as a column of labels in the input dataset or separately." ++PROGRAM_INFO("Mean Shift Clustering", ++ // Short description. ++ "A fast implementation of mean-shift clustering using dual-tree range " ++ "search. Given a dataset, this uses the mean shift algorithm to produce " ++ "and return a clustering of the data.", ++ // Long description. ++ "This program performs mean shift clustering on the given dataset, storing " ++ "the learned cluster assignments either as a column of labels in the input " ++ "dataset or separately." + "\n\n" + "The input dataset should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter, and the radius used for search" +@@ -42,7 +48,16 @@ PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " + PRINT_DATASET("data") + " and store the centroids to " + + PRINT_DATASET("centroids") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids")); ++ PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids"), ++ SEE_ALSO("@kmeans", "#kmeans"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Mean shift on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mean_shift"), ++ SEE_ALSO("Mean Shift, Mode Seeking, and Clustering (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.510.1222" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::mean_shift::MeanShift C++ class documentation", ++ "@doxygen/classmlpack_1_1meanshift_1_1MeanShift.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/naive_bayes/CMakeLists.txt b/src/mlpack/methods/naive_bayes/CMakeLists.txt +index f15dc229c..b2afe0707 100644 +--- a/src/mlpack/methods/naive_bayes/CMakeLists.txt ++++ b/src/mlpack/methods/naive_bayes/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nbc) + add_python_binding(nbc) ++add_markdown_docs(nbc "cli;python" "classification") +diff --git a/src/mlpack/methods/naive_bayes/nbc_main.cpp b/src/mlpack/methods/naive_bayes/nbc_main.cpp +index 68a4a70e0..612d675d0 100644 +--- a/src/mlpack/methods/naive_bayes/nbc_main.cpp ++++ b/src/mlpack/methods/naive_bayes/nbc_main.cpp +@@ -26,6 +26,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Parametric Naive Bayes Classifier", ++ // Short description. ++ "An implementation of the Naive Bayes Classifier, used for classification. " ++ "Given labeled data, an NBC model can be trained and saved, or, a " ++ "pre-trained model can be used for classification.", ++ // Long description. + "This program trains the Naive Bayes classifier on the given labeled " + "training set, or loads a model from the given model file, and then may use" + " that trained model to classify the points in a given test set." +@@ -66,7 +71,14 @@ PROGRAM_INFO("Parametric Naive Bayes Classifier", + "may be used:" + "\n\n" + + PRINT_CALL("nbc", "input_model", "nbc_model", "test", "test_set", "output", +- "predictions")); ++ "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Naive Bayes classifier on Wikipedia", ++ "https://en.wikipedia.org/wiki/Naive_Bayes_classifier"), ++ SEE_ALSO("mlpack::naive_bayes::NaiveBayesClassifier C++ class " ++ "documentation", "@doxygen/classmlpack_1_1naive__bayes_1_1" ++ "NaiveBayesClassifier.html")); + + // A struct for saving the model with mappings. + struct NBCModel +diff --git a/src/mlpack/methods/nca/CMakeLists.txt b/src/mlpack/methods/nca/CMakeLists.txt +index 777eef39b..7b1fd98f5 100644 +--- a/src/mlpack/methods/nca/CMakeLists.txt ++++ b/src/mlpack/methods/nca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nca) + add_python_binding(nca) ++add_markdown_docs(nca "cli;python" "transformations") +diff --git a/src/mlpack/methods/nca/nca_main.cpp b/src/mlpack/methods/nca/nca_main.cpp +index b1dd431ae..e26cae5e7 100644 +--- a/src/mlpack/methods/nca/nca_main.cpp ++++ b/src/mlpack/methods/nca/nca_main.cpp +@@ -22,6 +22,12 @@ + + // Define parameters. + PROGRAM_INFO("Neighborhood Components Analysis (NCA)", ++ // Short description. ++ "An implementation of neighborhood components analysis, a distance learning" ++ " technique that can be used for preprocessing. Given a labeled dataset, " ++ "this uses NCA, which seeks to improve the k-nearest-neighbor " ++ "classification, and returns the learned distance metric.", ++ // Long description. + "This program implements Neighborhood Components Analysis, both a linear " + "dimensionality reduction technique and a distance learning technique. The" + " method seeks to improve k-nearest-neighbor classification on a dataset " +@@ -82,7 +88,14 @@ PROGRAM_INFO("Neighborhood Components Analysis (NCA)", + "mlpack L-BFGS documentation (in lbfgs.hpp) or the vast set of published " + "literature on L-BFGS." + "\n\n" +- "By default, the SGD optimizer is used."); ++ "By default, the SGD optimizer is used.", ++ SEE_ALSO("Neighbourhood components analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Neighbourhood_components_analysis"), ++ SEE_ALSO("Neighbourhood components analysis (pdf)", ++ "http://papers.nips.cc/paper/2566-neighbourhood-components-" ++ "analysis.pdf"), ++ SEE_ALSO("mlpack::nca::NCA C++ class documentation", ++ "@doxygen/classmlpack_1_1nca_1_1NCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to run NCA on.", "i"); + PARAM_MATRIX_OUT("output", "Output matrix for learned distance matrix.", "o"); +diff --git a/src/mlpack/methods/neighbor_search/CMakeLists.txt b/src/mlpack/methods/neighbor_search/CMakeLists.txt +index 1958ebe17..789beb295 100644 +--- a/src/mlpack/methods/neighbor_search/CMakeLists.txt ++++ b/src/mlpack/methods/neighbor_search/CMakeLists.txt +@@ -29,5 +29,8 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # Add mlpack_knn and mlpack_kfn executables. + add_cli_executable(knn) + add_python_binding(knn) ++add_markdown_docs(knn "cli;python" "geometry") ++ + add_cli_executable(kfn) + add_python_binding(kfn) ++add_markdown_docs(kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp +index 56415c3ae..57e2ffdf6 100644 +--- a/src/mlpack/methods/neighbor_search/kfn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp +@@ -34,6 +34,12 @@ typedef NSModel KFNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Furthest-Neighbors Search", ++ // Short description. ++ "An implementation of k-furthest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k furthest neighbors in the reference set of each query" ++ " point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-furthest-neighbors of a set of " + "points. You may specify a separate set of reference points and query " + "points, or just a reference set which will be used as both the reference " +@@ -51,7 +57,13 @@ PROGRAM_INFO("k-Furthest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th furthest neighbor from the point in the " + "query set with index i. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@approx_kfn", "#approx_kfn"), ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp +index 336315dea..46a2b53bf 100644 +--- a/src/mlpack/methods/neighbor_search/knn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/knn_main.cpp +@@ -36,6 +36,12 @@ typedef NSModel KNNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Nearest-Neighbors Search", ++ // Short description. ++ "An implementation of k-nearest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k nearest neighbors in the reference set of each query " ++ "point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-nearest-neighbors of a set of " + "points using kd-trees or cover trees (cover tree support is experimental " + "and may be slow). You may specify a separate set of " +@@ -53,7 +59,16 @@ PROGRAM_INFO("k-Nearest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th nearest neighbor from the point in the " + "query set with index i. Row j and column i in the distances output matrix" +- " corresponds to the distance between those two points."); ++ " corresponds to the distance between those two points.", ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("@kfn", "#kfn"), ++ SEE_ALSO("NeighborSearch tutorial (k-nearest-neighbors)", ++ "@doxygen/nstutorial.html"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/nmf/CMakeLists.txt b/src/mlpack/methods/nmf/CMakeLists.txt +index 18e01f1b4..f1c4666f3 100644 +--- a/src/mlpack/methods/nmf/CMakeLists.txt ++++ b/src/mlpack/methods/nmf/CMakeLists.txt +@@ -1,2 +1,3 @@ + add_cli_executable(nmf) + add_python_binding(nmf) ++add_markdown_docs(nmf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/nmf/nmf_main.cpp b/src/mlpack/methods/nmf/nmf_main.cpp +index e39273eb2..1e9e4a539 100644 +--- a/src/mlpack/methods/nmf/nmf_main.cpp ++++ b/src/mlpack/methods/nmf/nmf_main.cpp +@@ -28,10 +28,15 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " +- "non-negative matrix factorization on the given dataset, storing the " +- "resulting decomposed matrices in the specified files. For an input " +- "dataset V, NMF decomposes V into two matrices W and H such that " ++PROGRAM_INFO("Non-negative Matrix Factorization", ++ // Short description. ++ "An implementation of non-negative matrix factorization. This can be used " ++ "to decompose an input dataset into two low-rank non-negative components.", ++ // Long description. ++ "This program performs non-negative matrix factorization on the given " ++ "dataset, storing the resulting decomposed matrices in the specified " ++ "files. For an input dataset V, NMF decomposes V into two matrices W " ++ "and H such that " + "\n\n" + "V = W * H" + "\n\n" +@@ -60,7 +65,17 @@ PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " + PRINT_DATASET("H") + ", the following command could be used: " + "\n\n" + + PRINT_CALL("nmf", "input", "V", "w", "W", "h", "H", "rank", 10, +- "update_rules", "multdist")); ++ "update_rules", "multdist"), ++ SEE_ALSO("@cf", "#cf"), ++ SEE_ALSO("Alternating matrix factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Non-negative matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Non-negative_matrix_factorization"), ++ SEE_ALSO("Algorithms for non-negative matrix factorization (pdf)", ++ "http://papers.nips.cc/paper/1861-algorithms-for-non-negative-matrix-" ++ "factorization.pdf"), ++ SEE_ALSO("mlpack::amf::AMF C++ class documentation", ++ "@doxygen/classmlpack_1_1amf_1_1AMF.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform NMF on.", "i"); +diff --git a/src/mlpack/methods/pca/CMakeLists.txt b/src/mlpack/methods/pca/CMakeLists.txt +index 097598ee8..8b317c4fc 100644 +--- a/src/mlpack/methods/pca/CMakeLists.txt ++++ b/src/mlpack/methods/pca/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(pca) + add_python_binding(pca) ++add_markdown_docs(pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/pca/pca_main.cpp b/src/mlpack/methods/pca/pca_main.cpp +index 4176f0680..493a80026 100644 +--- a/src/mlpack/methods/pca/pca_main.cpp ++++ b/src/mlpack/methods/pca/pca_main.cpp +@@ -26,12 +26,18 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Principal Components Analysis", "This program performs principal " +- "components analysis on the given dataset using the exact, randomized, " +- "randomized block Krylov, or QUIC SVD method. It will transform the data " +- "onto its principal components, optionally performing dimensionality " +- "reduction by ignoring the principal components with the smallest " +- "eigenvalues." ++PROGRAM_INFO("Principal Components Analysis", ++ // Short description. ++ "An implementation of several strategies for principal components analysis " ++ "(PCA), a common preprocessing step. Given a dataset and a desired new " ++ "dimensionality, this can reduce the dimensionality of the data using the " ++ "linear transformation determined by PCA.", ++ // Long description. ++ "This program performs principal components analysis on the given dataset " ++ "using the exact, randomized, randomized block Krylov, or QUIC SVD method. " ++ "It will transform the data onto its principal components, optionally " ++ "performing dimensionality reduction by ignoring the principal components " ++ "with the smallest eigenvalues." + "\n\n" + "Use the " + PRINT_PARAM_STRING("input") + " parameter to specify the " + "dataset to perform PCA on. A desired new dimensionality can be specified " +@@ -52,7 +58,11 @@ PROGRAM_INFO("Principal Components Analysis", "This program performs principal " + PRINT_DATASET("data_mod") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("pca", "input", "data", "new_dimensionality", 5, +- "decomposition_method", "randomized", "output", "data_mod")); ++ "decomposition_method", "randomized", "output", "data_mod"), ++ SEE_ALSO("Principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Principal_component_analysis"), ++ SEE_ALSO("mlpack::pca::PCA C++ class documentation", ++ "@doxygen/classmlpack_1_1pca_1_1PCA.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform PCA on.", "i"); +diff --git a/src/mlpack/methods/perceptron/CMakeLists.txt b/src/mlpack/methods/perceptron/CMakeLists.txt +index f0b4afc59..96a003562 100644 +--- a/src/mlpack/methods/perceptron/CMakeLists.txt ++++ b/src/mlpack/methods/perceptron/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(perceptron) + add_python_binding(perceptron) ++add_markdown_docs(perceptron "cli;python" "classification") +diff --git a/src/mlpack/methods/perceptron/perceptron_main.cpp b/src/mlpack/methods/perceptron/perceptron_main.cpp +index d23e6694b..5dde3939f 100644 +--- a/src/mlpack/methods/perceptron/perceptron_main.cpp ++++ b/src/mlpack/methods/perceptron/perceptron_main.cpp +@@ -26,6 +26,12 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Perceptron", ++ // Short description. ++ "An implementation of a perceptron---a single level neural network--=for " ++ "classification. Given labeled data, a perceptron can be trained and saved" ++ " for future use; or, a pre-trained perceptron can be used for " ++ "classification on new points.", ++ // Long description. + "This program implements a perceptron, which is a single level neural " + "network. The perceptron makes its predictions based on a linear predictor " + "function combining a set of weights with the feature vector. The " +@@ -75,7 +81,12 @@ PROGRAM_INFO("Perceptron", + "cannot pass a perceptron model trained on 2 classes and then re-train with" + " a 4-class dataset. Similarly, attempting classification on a " + "3-dimensional dataset with a perceptron that has been trained on 8 " +- "dimensions will cause an error."); ++ "dimensions will cause an error.", ++ SEE_ALSO("@adaboost", "#adaboost"), ++ SEE_ALSO("Perceptron on Wikipedia", ++ "https://en.wikipedia.org/wiki/Perceptron"), ++ SEE_ALSO("mlpack::perceptron::Perceptron C++ class documentation", ++ "@doxygen/classmlpack_1_1perceptron_1_1Perceptron.html")); + + // When we save a model, we must also save the class mappings. So we use this + // auxiliary structure to store both the perceptron and the mapping, and we'll +diff --git a/src/mlpack/methods/preprocess/CMakeLists.txt b/src/mlpack/methods/preprocess/CMakeLists.txt +index af9299c64..a65487981 100644 +--- a/src/mlpack/methods/preprocess/CMakeLists.txt ++++ b/src/mlpack/methods/preprocess/CMakeLists.txt +@@ -15,10 +15,17 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + #add_cli_executable(preprocess_stats) + add_cli_executable(preprocess_split) + add_python_binding(preprocess_split) ++add_markdown_docs(preprocess_split "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_binarize) + add_python_binding(preprocess_binarize) ++add_markdown_docs(preprocess_binarize "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_describe) + add_python_binding(preprocess_describe) ++add_markdown_docs(preprocess_describe "cli;python" "preprocessing") ++ + #add_cli_executable(preprocess_scan) + add_cli_executable(preprocess_imputer) + #add_python_binding(preprocess_imputer) ++add_markdown_docs(preprocess_imputer "cli" "preprocessing") +diff --git a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +index 53e6fb056..9d69834af 100644 +--- a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +@@ -14,7 +14,13 @@ + #include + #include + +-PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " ++PROGRAM_INFO("Binarize Data", ++ // Short description. ++ "A utility to binarize a dataset. Given a dataset, this utility converts " ++ "each value in the desired dimension(s) to 0 or 1; this can be a useful " ++ "preprocessing step.", ++ // Long description. ++ "This utility takes a dataset and binarizes the " + "variables into either 0 or 1 given threshold. User can apply binarization " + "on a dimension or the whole dataset. The dimension to apply binarization " + "to can be specified using the " + PRINT_PARAM_STRING("dimension") + +@@ -38,7 +44,10 @@ PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " + PRINT_DATASET("X") + ", we could instead run" + "\n\n" + + PRINT_CALL("preprocess_binarize", "input", "X", "threshold", 5.0, +- "dimension", 0, "output", "Y")); ++ "dimension", 0, "output", "Y"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +index 744f826a5..43b6df7f5 100644 +--- a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +@@ -22,12 +22,17 @@ using namespace mlpack::util; + using namespace std; + using namespace boost; + +-PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " +- "prints out the descriptive statistics of the data. Descriptive statistics " +- "is the discipline of quantitatively describing the main features of a " +- "collection of information, or the quantitative description itself. The " +- "program does not modify the original file, but instead prints out the " +- "statistics to the console. The printed result will look like a table." ++PROGRAM_INFO("Descriptive Statistics", ++ // Short description. ++ "A utility for printing descriptive statistics about a dataset. This " ++ "prints a number of details about a dataset in a tabular format.", ++ // Long description. ++ "This utility takes a dataset and prints out the descriptive statistics " ++ "of the data. Descriptive statistics is the discipline of quantitatively " ++ "describing the main features of a collection of information, or the " ++ "quantitative description itself. The program does not modify the original " ++ "file, but instead prints out the statistics to the console. The printed " ++ "result will look like a table." + "\n\n" + "Optionally, width and precision of the output can be adjusted by a user " + "using the " + PRINT_PARAM_STRING("width") + " and " + +@@ -47,7 +52,10 @@ PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " + "the dataset as a population, we could run" + "\n\n" + + PRINT_CALL("preprocess_describe", "input", "X", "width", 10, "precision", 5, +- "verbose", true)); ++ "verbose", true), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data,", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +index 848bbe9cf..1825334ad 100644 +--- a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +@@ -23,8 +23,14 @@ + #include + #include + +-PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " +- "defined missing variable to another to provide more meaningful analysis " ++PROGRAM_INFO("Impute Data", ++ // Short description. ++ "This utility provides several imputation strategies for missing data. " ++ "Given a dataset with missing values, this can impute according to several " ++ "strategies, including user-defined values.", ++ // Long description. ++ "This utility takes a dataset and converts a user-defined missing variable " ++ "to another to provide more meaningful analysis." + "\n\n" + "The program does not modify the original file, but instead makes a " + "separate file to save the output data; You can save the output by " +@@ -35,7 +41,10 @@ PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " + "column-wise dataset, and save the result to result.csv, we could run" + "\n\n" + "$ mlpack_preprocess_imputer -i dataset.csv -o result.csv -m NULL -d 0 \n" +- "> -s listwise_deletion"); ++ "> -s listwise_deletion", ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + PARAM_STRING_IN_REQ("input_file", "File containing data.", "i"); + PARAM_STRING_OUT("output_file", "File to save output into.", "o"); +diff --git a/src/mlpack/methods/preprocess/preprocess_split_main.cpp b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +index e789e0250..4b510b790 100644 +--- a/src/mlpack/methods/preprocess/preprocess_split_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +@@ -15,11 +15,16 @@ + #include + #include + +-PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " +- "and splits them into a training set and a test set. Before the split, the " +- "points in the dataset are randomly reordered. The percentage of the " +- "dataset to be used as the test set can be specified with the " + +- PRINT_PARAM_STRING("test_ratio") + " parameter; the default is 0.2 (20%)." ++PROGRAM_INFO("Split Data", ++ // Short description. ++ "A utility to split data into a training and testing dataset. This can " ++ "also split labels according to the same split.", ++ // Long description. ++ "This utility takes a dataset and optionally labels and splits them into a " ++ "training set and a test set. Before the split, the points in the dataset " ++ "are randomly reordered. The percentage of the dataset to be used as the " ++ "test set can be specified with the " + PRINT_PARAM_STRING("test_ratio") + ++ " parameter; the default is 0.2 (20%)." + "\n\n" + "The output training and test matrices may be saved with the " + + PRINT_PARAM_STRING("training") + " and " + PRINT_PARAM_STRING("test") + +@@ -48,7 +53,10 @@ PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " + "\n\n" + + PRINT_CALL("preprocess_split", "input", "X", "input_labels", "y", + "test_ratio", 0.3, "training", "X_train", "training_labels", "y_train", +- "test", "X_test", "test_labels", "y_test")); ++ "test", "X_test", "test_labels", "y_test"), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data.", "i"); +diff --git a/src/mlpack/methods/radical/CMakeLists.txt b/src/mlpack/methods/radical/CMakeLists.txt +index 886566b11..c7dc29cac 100644 +--- a/src/mlpack/methods/radical/CMakeLists.txt ++++ b/src/mlpack/methods/radical/CMakeLists.txt +@@ -15,3 +15,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(radical) + add_python_binding(radical) ++add_markdown_docs(radical "cli;python" "transformations") +diff --git a/src/mlpack/methods/radical/radical_main.cpp b/src/mlpack/methods/radical/radical_main.cpp +index 129b3d7ac..7a2a51e36 100644 +--- a/src/mlpack/methods/radical/radical_main.cpp ++++ b/src/mlpack/methods/radical/radical_main.cpp +@@ -15,11 +15,18 @@ + #include + #include "radical.hpp" + +-PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" +- "component analysis (ICA). Assuming that we have an input matrix X, the" +- "goal is to find a square unmixing matrix W such that Y = W * X and the " +- "dimensions of Y are independent components. If the algorithm is running" +- "particularly slowly, try reducing the number of replicates." ++PROGRAM_INFO("RADICAL", ++ // Short description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Given a dataset, this can decompose the dataset into an unmixing " ++ "matrix and an independent component matrix; this can be useful for " ++ "preprocessing.", ++ // Long description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Assuming that we have an input matrix X, the goal is to find a " ++ "square unmixing matrix W such that Y = W * X and the dimensions of Y are " ++ "independent components. If the algorithm is running particularly slowly, " ++ "try reducing the number of replicates." + "\n\n" + "The input matrix to perform ICA on should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter. The output matrix Y may be " +@@ -31,7 +38,14 @@ PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" + "40 replicates, saving the independent components to " + + PRINT_DATASET("ic") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic")); ++ PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic"), ++ SEE_ALSO("Independent component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Independent_component_analysis"), ++ SEE_ALSO("ICA using spacings estimates of entropy (pdf)", ++ "http://www.jmlr.org/papers/volume4/learned-miller03a/" ++ "learned-miller03a.pdf"), ++ SEE_ALSO("mlpack::radical::Radical C++ class documentation", ++ "@doxygen/classmlpack_1_1radical_1_1Radical.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset for ICA.", "i"); + +diff --git a/src/mlpack/methods/random_forest/CMakeLists.txt b/src/mlpack/methods/random_forest/CMakeLists.txt +index 3ac70de8a..89b03837d 100644 +--- a/src/mlpack/methods/random_forest/CMakeLists.txt ++++ b/src/mlpack/methods/random_forest/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(random_forest) + add_python_binding(random_forest) ++add_markdown_docs(random_forest "cli;python" "classification") +diff --git a/src/mlpack/methods/random_forest/random_forest_main.cpp b/src/mlpack/methods/random_forest/random_forest_main.cpp +index 2ca69b853..4915f1cd5 100644 +--- a/src/mlpack/methods/random_forest/random_forest_main.cpp ++++ b/src/mlpack/methods/random_forest/random_forest_main.cpp +@@ -20,6 +20,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Random forests", ++ // Short description. ++ "An implementation of the standard random forest algorithm by Leo Breiman " ++ "for classification. Given labeled data, a random forest can be trained " ++ "and saved for future use; or, a pre-trained random forest can be used for " ++ "classification.", ++ // Long description. + "This program is an implementation of the standard random forest " + "classification algorithm by Leo Breiman. A random forest can be " + "trained and saved for later use, or a random forest may be loaded " +@@ -70,7 +76,16 @@ PROGRAM_INFO("Random forests", + "could call " + "\n\n" + + PRINT_CALL("random_forest", "input_model", "rf_model", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@hoeffding_tree", "#hoeffding_tree"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("Random forest on Wikipedia", ++ "https://en.wikipedia.org/wiki/Random_forest"), ++ SEE_ALSO("Random forests (pdf)", ++ "https://link.springer.com/content/pdf/10.1023/A:1010933404324.pdf"), ++ SEE_ALSO("mlpack::tree::RandomForest C++ class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1RandomForest.html")); + + PARAM_MATRIX_IN("training", "Training dataset.", "t"); + PARAM_UROW_IN("labels", "Labels for training dataset.", "l"); +diff --git a/src/mlpack/methods/range_search/CMakeLists.txt b/src/mlpack/methods/range_search/CMakeLists.txt +index 9d29343db..68a020315 100644 +--- a/src/mlpack/methods/range_search/CMakeLists.txt ++++ b/src/mlpack/methods/range_search/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(range_search) + #add_python_binding(range_search) ++add_markdown_docs(range_search "cli" "geometry") +diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp +index 27e49caf1..c50bb709b 100644 +--- a/src/mlpack/methods/range_search/range_search_main.cpp ++++ b/src/mlpack/methods/range_search/range_search_main.cpp +@@ -29,6 +29,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("Range Search", ++ // Short description. ++ "An implementation of range search with single-tree and dual-tree " ++ "algorithms. Given a set of reference points and a set of query points and" ++ " a range, this can find the set of reference points within the desired " ++ "range for each query point, and any trees built during the computation can" ++ " be saved for reuse with future range searches.", ++ // Long description. + "This program implements range search with a Euclidean distance metric. " + "For a given query point, a given range, and a given set of reference " + "points, the program will return all of the reference points with distance " +@@ -55,7 +62,14 @@ PROGRAM_INFO("Range Search", + " resultant CSV-like files may not be loadable by many programs. However, " + "at this time a better way to store this non-square result is not known. " + "As a result, any output files will be written as CSVs in this manner, " +- "regardless of the given extension."); ++ "regardless of the given extension.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Range searching on Wikipedia", ++ "https://en.wikipedia.org/wiki/Range_searching"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::range::RangeSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1range_1_1RangeSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/rann/CMakeLists.txt b/src/mlpack/methods/rann/CMakeLists.txt +index 457a11dd6..3ba78994a 100644 +--- a/src/mlpack/methods/rann/CMakeLists.txt ++++ b/src/mlpack/methods/rann/CMakeLists.txt +@@ -37,3 +37,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # reference sets. + add_cli_executable(krann) + add_python_binding(krann) ++add_markdown_docs(krann "cli;python" "geometry") +diff --git a/src/mlpack/methods/rann/krann_main.cpp b/src/mlpack/methods/rann/krann_main.cpp +index 8224a2113..94661a53f 100644 +--- a/src/mlpack/methods/rann/krann_main.cpp ++++ b/src/mlpack/methods/rann/krann_main.cpp +@@ -30,6 +30,13 @@ typedef RAModel RANNModel; + + // Information about the program itself. + PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", ++ // Short description. ++ "An implementation of rank-approximate k-nearest-neighbor search (kRANN) " ++ " using single-tree and dual-tree algorithms. Given a set of reference " ++ "points and query points, this can find the k nearest neighbors in the " ++ "reference set of each query point using trees; trees that are built can " ++ "be saved for future use.", ++ // Long description. + "This program will calculate the k rank-approximate-nearest-neighbors of a " + "set of points. You may specify a separate set of reference points and " + "query points, or just a reference set which will be used as both the " +@@ -55,7 +62,15 @@ PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", + "neighbors output file corresponds to the index of the point in the " + "reference set which is the i'th nearest neighbor from the point in the " + "query set with index j. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("Rank-approximate nearest neighbor search: Retaining meaning and " ++ "speed in high dimensions (pdf)", "https://papers.nips.cc/paper/3864-" ++ "rank-approximate-nearest-neighbor-search-retaining-meaning-and-speed-" ++ "in-high-dimensions.pdf"), ++ SEE_ALSO("mlpack::neighbor::RASearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1RASearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/softmax_regression/CMakeLists.txt b/src/mlpack/methods/softmax_regression/CMakeLists.txt +index c6770f7c4..60a7d4cff 100644 +--- a/src/mlpack/methods/softmax_regression/CMakeLists.txt ++++ b/src/mlpack/methods/softmax_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(softmax_regression) + add_python_binding(softmax_regression) ++add_markdown_docs(softmax_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +index 97b54a2a5..4e3f88e76 100644 +--- a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp ++++ b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +@@ -24,11 +24,18 @@ using namespace mlpack::regression; + using namespace mlpack::util; + + // Define parameters for the executable. +-PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " +- "a generalization of logistic regression to the multiclass case, and has " +- "support for L2 regularization. The program is able to train a model, load" +- " an existing model, and give predictions (and optionally their accuracy) " +- "for test data." ++PROGRAM_INFO("Softmax Regression", ++ // Short description. ++ "An implementation of softmax regression for classification, which is a " ++ "multiclass generalization of logistic regression. Given labeled data, a " ++ "softmax regression model can be trained and saved for future use, or, a " ++ "pre-trained softmax regression model can be used for classification of " ++ "new points.", ++ // Long description. ++ "This program performs softmax regression, a generalization of logistic " ++ "regression to the multiclass case, and has support for L2 regularization. " ++ " The program is able to train a model, load an existing model, and give " ++ "predictions (and optionally their accuracy) for test data." + "\n\n" + "Training a softmax regression model is done by giving a file of training " + "points with the " + PRINT_PARAM_STRING("training") + " parameter and their" +@@ -71,7 +78,14 @@ PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " + " " + PRINT_DATASET("predictions") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("softmax_regression", "input_model", "sr_model", "test", +- "test_points", "predictions", "predictions")); ++ "test_points", "predictions", "predictions"), ++ SEE_ALSO("@logistic_regression", "#logistic_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Multinomial logistic regression (softmax regression) on " ++ "Wikipedia", ++ "https://en.wikipedia.org/wiki/Multinomial_logistic_regression"), ++ SEE_ALSO("mlpack::regression::SoftmaxRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1SoftmaxRegression.html")); + + // Required options. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/sparse_coding/CMakeLists.txt b/src/mlpack/methods/sparse_coding/CMakeLists.txt +index cfd1108cb..7bb0f7b79 100644 +--- a/src/mlpack/methods/sparse_coding/CMakeLists.txt ++++ b/src/mlpack/methods/sparse_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(sparse_coding) + add_python_binding(sparse_coding) ++add_markdown_docs(sparse_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +index e4b561f53..515cb78eb 100644 +--- a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp ++++ b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +@@ -22,12 +22,20 @@ using namespace mlpack::math; + using namespace mlpack::sparse_coding; + using namespace mlpack::util; + +-PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " +- "Dictionary Learning, which achieves sparsity via an l1-norm regularizer on" +- " the codes (LASSO) or an (l1+l2)-norm regularizer on the codes (the " +- "Elastic Net). Given a dense data matrix X with d dimensions and n points," +- " sparse coding seeks to find a dense dictionary matrix D with k atoms in " +- "d dimensions, and a sparse coding matrix Z with n points in k dimensions." ++PROGRAM_INFO("Sparse Coding", ++ // Short description. ++ "An implementation of Sparse Coding with Dictionary Learning. Given a " ++ "dataset, this will decompose the dataset into a sparse combination of a " ++ "few dictionary elements, where the dictionary is learned during " ++ "computation; a dictionary can be reused for future sparse coding of new " ++ "points.", ++ // Long description. ++ "An implementation of Sparse Coding with Dictionary Learning, which " ++ "achieves sparsity via an l1-norm regularizer on the codes (LASSO) or an " ++ "(l1+l2)-norm regularizer on the codes (the Elastic Net). Given a dense " ++ "data matrix X with d dimensions and n points, sparse coding seeks to find " ++ "a dense dictionary matrix D with k atoms in d dimensions, and a sparse " ++ "coding matrix Z with n points in k dimensions." + "\n\n" + "The original data matrix X can then be reconstructed as Z * D. Therefore," + " this program finds a representation of each point in X as a sparse linear" +@@ -62,7 +70,18 @@ PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " + PRINT_DATASET("codes") + ": " + "\n\n" + + PRINT_CALL("sparse_coding", "input_model", "model", "test", "otherdata", +- "codes", "codes")); ++ "codes", "codes"), ++ SEE_ALSO("@local_coordinate_coding", "#local_coordinate_coding"), ++ SEE_ALSO("Sparse dictionary learning on Wikipedia", ++ "https://en.wikipedia.org/wiki/Sparse_dictionary_learning"), ++ SEE_ALSO("Efficient sparse coding algorithms (pdf)", ++ "http://papers.nips.cc/paper/2979-efficient-sparse-coding-" ++ "algorithms.pdf"), ++ SEE_ALSO("Regularization and variable selection via the elastic net", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4696&" ++ "rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::sparse_coding::SparseCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1sparse__coding_1_1SparseCoding.html")); + + // Train the model. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +-- +2.20.1 + diff --git a/_src/markdown-patches/mlpack-3.0.4-markdown.patch b/_src/markdown-patches/mlpack-3.0.4-markdown.patch new file mode 100644 index 000000000..05e2c0656 --- /dev/null +++ b/_src/markdown-patches/mlpack-3.0.4-markdown.patch @@ -0,0 +1,7190 @@ +From 80d6f77d88093edb380ca9e34181af174fcaf3f4 Mon Sep 17 00:00:00 2001 +From: Ryan Curtin +Date: Wed, 6 Mar 2019 19:56:30 -0500 +Subject: [PATCH] Backport Markdown support onto mlpack 3.0.4. + +--- + CMake/RunProgram.cmake | 8 + + CMakeLists.txt | 10 +- + src/mlpack/CMakeLists.txt | 9 +- + src/mlpack/bindings/CMakeLists.txt | 2 + + src/mlpack/bindings/cli/CMakeLists.txt | 2 + + .../bindings/cli/default_param_impl.hpp | 59 +- + src/mlpack/bindings/cli/end_program.hpp | 2 +- + .../bindings/cli/get_printable_type.hpp | 79 +++ + .../bindings/cli/get_printable_type_impl.hpp | 109 ++++ + .../bindings/cli/print_doc_functions.hpp | 38 ++ + .../bindings/cli/print_doc_functions_impl.hpp | 110 +++- + src/mlpack/bindings/cli/print_help.cpp | 6 +- + src/mlpack/bindings/cli/print_type_doc.hpp | 81 +++ + .../bindings/cli/print_type_doc_impl.hpp | 173 ++++++ + src/mlpack/bindings/cli/string_type_param.hpp | 6 - + .../bindings/cli/string_type_param_impl.hpp | 10 - + src/mlpack/bindings/markdown/CMakeLists.txt | 209 +++++++ + .../markdown/MarkdownCategories.cmake | 11 + + src/mlpack/bindings/markdown/binding_info.cpp | 49 ++ + src/mlpack/bindings/markdown/binding_info.hpp | 62 ++ + .../bindings/markdown/default_param.hpp | 45 ++ + .../markdown/generate_markdown.binding.cpp.in | 46 ++ + .../markdown/generate_markdown.binding.hpp.in | 28 + + .../markdown/generate_markdown.cpp.in | 114 ++++ + .../bindings/markdown/get_binding_name.cpp | 40 ++ + .../bindings/markdown/get_binding_name.hpp | 30 + + src/mlpack/bindings/markdown/get_param.hpp | 38 ++ + .../bindings/markdown/get_printable_param.hpp | 126 ++++ + .../markdown/get_printable_param_name.hpp | 83 +++ + .../get_printable_param_name_impl.hpp | 79 +++ + .../markdown/get_printable_param_value.hpp | 88 +++ + .../get_printable_param_value_impl.hpp | 84 +++ + .../bindings/markdown/get_printable_type.hpp | 62 ++ + .../bindings/markdown/is_serializable.hpp | 63 ++ + src/mlpack/bindings/markdown/md_option.hpp | 109 ++++ + .../bindings/markdown/print_doc_functions.hpp | 109 ++++ + .../markdown/print_doc_functions_impl.hpp | 540 ++++++++++++++++++ + src/mlpack/bindings/markdown/print_docs.cpp | 218 +++++++ + src/mlpack/bindings/markdown/print_docs.hpp | 33 ++ + .../bindings/markdown/print_type_doc.hpp | 46 ++ + .../bindings/markdown/program_doc_wrapper.hpp | 41 ++ + .../bindings/markdown/res/change_language.js | 102 ++++ + .../bindings/markdown/res/formatting.css | 142 +++++ + src/mlpack/bindings/markdown/res/menu_bg.png | Bin 0 -> 395 bytes + src/mlpack/bindings/python/CMakeLists.txt | 4 + + src/mlpack/bindings/python/default_param.hpp | 95 +++ + .../bindings/python/default_param_impl.hpp | 147 +++++ + .../bindings/python/get_cython_type.hpp | 10 - + .../bindings/python/get_printable_type.hpp | 120 ++++ + .../python/get_printable_type_impl.hpp | 151 +++++ + src/mlpack/bindings/python/print_doc.hpp | 22 +- + .../bindings/python/print_doc_functions.hpp | 26 + + .../python/print_doc_functions_impl.hpp | 124 +++- + .../python/print_input_processing.hpp | 1 - + src/mlpack/bindings/python/print_type_doc.hpp | 81 +++ + .../bindings/python/print_type_doc_impl.hpp | 162 ++++++ + src/mlpack/bindings/python/py_option.hpp | 4 + + .../python/tests/test_python_binding_main.cpp | 1 + + src/mlpack/core/util/cli.cpp | 3 +- + src/mlpack/core/util/mlpack_main.hpp | 68 ++- + src/mlpack/core/util/param.hpp | 31 +- + src/mlpack/core/util/program_doc.cpp | 30 +- + src/mlpack/core/util/program_doc.hpp | 17 +- + src/mlpack/methods/CMakeLists.txt | 4 + + src/mlpack/methods/adaboost/CMakeLists.txt | 1 + + src/mlpack/methods/adaboost/adaboost_main.cpp | 21 +- + src/mlpack/methods/approx_kfn/CMakeLists.txt | 1 + + .../methods/approx_kfn/approx_kfn_main.cpp | 19 +- + src/mlpack/methods/cf/CMakeLists.txt | 1 + + src/mlpack/methods/cf/cf_main.cpp | 25 +- + src/mlpack/methods/dbscan/CMakeLists.txt | 1 + + src/mlpack/methods/dbscan/dbscan_main.cpp | 12 +- + .../methods/decision_stump/CMakeLists.txt | 1 + + .../decision_stump/decision_stump_main.cpp | 12 +- + .../methods/decision_tree/CMakeLists.txt | 1 + + .../decision_tree/decision_tree_main.cpp | 16 +- + src/mlpack/methods/det/CMakeLists.txt | 1 + + src/mlpack/methods/det/det_main.cpp | 15 +- + src/mlpack/methods/emst/CMakeLists.txt | 1 + + src/mlpack/methods/emst/emst_main.cpp | 13 +- + src/mlpack/methods/fastmks/CMakeLists.txt | 1 + + src/mlpack/methods/fastmks/fastmks_main.cpp | 15 +- + src/mlpack/methods/gmm/CMakeLists.txt | 5 + + src/mlpack/methods/gmm/gmm_generate_main.cpp | 12 +- + .../methods/gmm/gmm_probability_main.cpp | 13 +- + src/mlpack/methods/gmm/gmm_train_main.cpp | 13 +- + src/mlpack/methods/hmm/CMakeLists.txt | 7 + + src/mlpack/methods/hmm/hmm_generate_main.cpp | 18 +- + src/mlpack/methods/hmm/hmm_loglik_main.cpp | 19 +- + src/mlpack/methods/hmm/hmm_train_main.cpp | 21 +- + src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 19 +- + .../methods/hoeffding_trees/CMakeLists.txt | 1 + + .../hoeffding_trees/hoeffding_tree_main.cpp | 14 +- + src/mlpack/methods/kernel_pca/CMakeLists.txt | 1 + + .../methods/kernel_pca/kernel_pca_main.cpp | 17 +- + src/mlpack/methods/kmeans/CMakeLists.txt | 1 + + src/mlpack/methods/kmeans/kmeans_main.cpp | 32 +- + src/mlpack/methods/lars/CMakeLists.txt | 1 + + src/mlpack/methods/lars/lars_main.cpp | 21 +- + .../methods/linear_regression/CMakeLists.txt | 1 + + .../linear_regression_main.cpp | 14 +- + .../local_coordinate_coding/CMakeLists.txt | 1 + + .../local_coordinate_coding_main.cpp | 14 +- + .../logistic_regression/CMakeLists.txt | 1 + + .../logistic_regression_main.cpp | 17 +- + src/mlpack/methods/lsh/CMakeLists.txt | 1 + + src/mlpack/methods/lsh/lsh_main.cpp | 17 +- + src/mlpack/methods/mean_shift/CMakeLists.txt | 1 + + .../methods/mean_shift/mean_shift_main.cpp | 23 +- + src/mlpack/methods/naive_bayes/CMakeLists.txt | 1 + + src/mlpack/methods/naive_bayes/nbc_main.cpp | 14 +- + src/mlpack/methods/nca/CMakeLists.txt | 1 + + src/mlpack/methods/nca/nca_main.cpp | 15 +- + .../methods/neighbor_search/CMakeLists.txt | 3 + + .../methods/neighbor_search/kfn_main.cpp | 14 +- + .../methods/neighbor_search/knn_main.cpp | 17 +- + src/mlpack/methods/nmf/CMakeLists.txt | 1 + + src/mlpack/methods/nmf/nmf_main.cpp | 25 +- + src/mlpack/methods/pca/CMakeLists.txt | 1 + + src/mlpack/methods/pca/pca_main.cpp | 24 +- + src/mlpack/methods/perceptron/CMakeLists.txt | 1 + + .../methods/perceptron/perceptron_main.cpp | 13 +- + src/mlpack/methods/preprocess/CMakeLists.txt | 7 + + .../preprocess/preprocess_binarize_main.cpp | 13 +- + .../preprocess/preprocess_describe_main.cpp | 22 +- + .../preprocess/preprocess_imputer_main.cpp | 15 +- + .../preprocess/preprocess_split_main.cpp | 20 +- + src/mlpack/methods/radical/CMakeLists.txt | 1 + + src/mlpack/methods/radical/radical_main.cpp | 26 +- + .../methods/random_forest/CMakeLists.txt | 1 + + .../random_forest/random_forest_main.cpp | 17 +- + .../methods/range_search/CMakeLists.txt | 1 + + .../range_search/range_search_main.cpp | 16 +- + src/mlpack/methods/rann/CMakeLists.txt | 1 + + src/mlpack/methods/rann/krann_main.cpp | 17 +- + .../methods/softmax_regression/CMakeLists.txt | 1 + + .../softmax_regression_main.cpp | 26 +- + .../methods/sparse_coding/CMakeLists.txt | 1 + + .../sparse_coding/sparse_coding_main.cpp | 33 +- + 139 files changed, 5005 insertions(+), 207 deletions(-) + create mode 100644 CMake/RunProgram.cmake + create mode 100644 src/mlpack/bindings/cli/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/cli/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/cli/print_type_doc_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/CMakeLists.txt + create mode 100644 src/mlpack/bindings/markdown/MarkdownCategories.cmake + create mode 100644 src/mlpack/bindings/markdown/binding_info.cpp + create mode 100644 src/mlpack/bindings/markdown/binding_info.hpp + create mode 100644 src/mlpack/bindings/markdown/default_param.hpp + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in + create mode 100644 src/mlpack/bindings/markdown/generate_markdown.cpp.in + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.cpp + create mode 100644 src/mlpack/bindings/markdown/get_binding_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/markdown/is_serializable.hpp + create mode 100644 src/mlpack/bindings/markdown/md_option.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions.hpp + create mode 100644 src/mlpack/bindings/markdown/print_doc_functions_impl.hpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.cpp + create mode 100644 src/mlpack/bindings/markdown/print_docs.hpp + create mode 100644 src/mlpack/bindings/markdown/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/markdown/program_doc_wrapper.hpp + create mode 100644 src/mlpack/bindings/markdown/res/change_language.js + create mode 100644 src/mlpack/bindings/markdown/res/formatting.css + create mode 100644 src/mlpack/bindings/markdown/res/menu_bg.png + create mode 100644 src/mlpack/bindings/python/default_param.hpp + create mode 100644 src/mlpack/bindings/python/default_param_impl.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type.hpp + create mode 100644 src/mlpack/bindings/python/get_printable_type_impl.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc.hpp + create mode 100644 src/mlpack/bindings/python/print_type_doc_impl.hpp + +diff --git a/CMake/RunProgram.cmake b/CMake/RunProgram.cmake +new file mode 100644 +index 000000000..6ae385314 +--- /dev/null ++++ b/CMake/RunProgram.cmake +@@ -0,0 +1,8 @@ ++# RunProgram.cmake: a CMake script that actually runs the given program to ++# generate a file, which is output into the given directory. ++# ++# This script depends on the following arguments: ++# ++# PROGRAM: the program to run to. ++# OUTPUT_FILE: the file to store the output in. ++execute_process(COMMAND ${PROGRAM} OUTPUT_FILE ${OUTPUT_FILE}) +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 5c5ffbe4a..843cae1f6 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -17,13 +17,19 @@ option(BUILD_CLI_EXECUTABLES "Build command-line executables." ON) + # set BUILD_PYTHON_BINDINGS to OFF when the platform is Windows. + if (WIN32) + option(BUILD_PYTHON_BINDINGS "Build Python bindings." OFF) ++ option(BUILD_SHARED_LIBS ++ "Compile shared libraries (if OFF, static libraries are compiled)." OFF) + message(WARNING "By default Python bindings are not compiled for Windows because they are not known to work. Set BUILD_PYTHON_BINDINGS to ON if you want them built.") + else () + option(BUILD_PYTHON_BINDINGS "Build Python bindings." ON) ++ option(BUILD_SHARED_LIBS ++ "Compile shared libraries (if OFF, static libraries are compiled)." ON) + endif() + +-option(BUILD_SHARED_LIBS +- "Compile shared libraries (if OFF, static libraries are compiled)." ON) ++# Build Markdown bindings for documentation. This is used as part of website ++# generation. ++option(BUILD_MARKDOWN_BINDINGS "Build Markdown bindings for website documentation." OFF) ++ + option(BUILD_WITH_COVERAGE + "Build with support for code coverage tools (gcc only)." OFF) + option(MATHJAX +diff --git a/src/mlpack/CMakeLists.txt b/src/mlpack/CMakeLists.txt +index fa8ce2ac2..605615025 100644 +--- a/src/mlpack/CMakeLists.txt ++++ b/src/mlpack/CMakeLists.txt +@@ -14,7 +14,7 @@ set(DIRS + ) + + foreach(dir ${DIRS}) +- add_subdirectory(${dir}) ++ add_subdirectory(${dir}) + endforeach() + + # MLPACK_SRCS is set in the subdirectories. The dependencies (MLPACK_LIBRARIES) +@@ -40,8 +40,7 @@ if (NOT BUILD_SHARED_LIBS) + add_definitions(-DMLPACK_STATIC_DEFINE) + endif () + +-target_link_libraries(mlpack +- ${MLPACK_LIBRARIES}) ++target_link_libraries(mlpack ${MLPACK_LIBRARIES}) + + set_target_properties(mlpack + PROPERTIES +@@ -123,3 +122,7 @@ if (BUILD_PYTHON_BINDINGS) + configure_file(${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/setup.py.in + ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py) + endif () ++ ++# If we are building Markdown documentation, we have to run some setup after we ++# recurse into methods/. If not, this function is empty. ++post_markdown_setup() +diff --git a/src/mlpack/bindings/CMakeLists.txt b/src/mlpack/bindings/CMakeLists.txt +index f35249ddd..e726c515b 100644 +--- a/src/mlpack/bindings/CMakeLists.txt ++++ b/src/mlpack/bindings/CMakeLists.txt +@@ -1,6 +1,7 @@ + # All we have to do is recurse into the subdirectories. + set(DIRS + cli ++ markdown + python + tests + ) +@@ -9,6 +10,7 @@ foreach(dir ${DIRS}) + add_subdirectory(${dir}) + endforeach() + ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) + set(MLPACK_SRCS ${MLPACK_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) + set(DISABLE_CFLAGS ${DISABLE_CFLAGS} PARENT_SCOPE) +diff --git a/src/mlpack/bindings/cli/CMakeLists.txt b/src/mlpack/bindings/cli/CMakeLists.txt +index 90563a4b9..c474b2916 100644 +--- a/src/mlpack/bindings/cli/CMakeLists.txt ++++ b/src/mlpack/bindings/cli/CMakeLists.txt +@@ -25,6 +25,8 @@ set(SOURCES + print_doc_functions_impl.hpp + print_help.hpp + print_help.cpp ++ print_type_doc.hpp ++ print_type_doc_impl.hpp + set_param.hpp + string_type_param.hpp + string_type_param_impl.hpp +diff --git a/src/mlpack/bindings/cli/default_param_impl.hpp b/src/mlpack/bindings/cli/default_param_impl.hpp +index 8c1740c1f..8c9ea7896 100644 +--- a/src/mlpack/bindings/cli/default_param_impl.hpp ++++ b/src/mlpack/bindings/cli/default_param_impl.hpp +@@ -32,7 +32,9 @@ std::string DefaultParamImpl( + std::tuple>>::type* /* junk */) + { + std::ostringstream oss; +- oss << boost::any_cast(data.value); ++ if (!std::is_same::value) ++ oss << boost::any_cast(data.value); ++ + return oss.str(); + } + +@@ -48,9 +50,35 @@ std::string DefaultParamImpl( + std::ostringstream oss; + const T& vector = boost::any_cast(data.value); + oss << "["; +- for (size_t i = 0; i < vector.size() - 1; ++i) +- oss << vector[i] << " "; +- oss << vector[vector.size() - 1] << "]"; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ + return oss.str(); + } + +@@ -67,39 +95,30 @@ std::string DefaultParamImpl( + } + + /** +- * Return the default value of a matrix option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a matrix option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::enable_if_c< + arma::is_arma_type::value || + std::is_same>::value>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ // The filename will always be empty. ++ return "''"; + } + + /** +- * Return the default value of a model option (this returns the default +- * filename, or '' if the default is no file). ++ * Return the default value of a model option (an empty filename). + */ + template + std::string DefaultParamImpl( +- const util::ParamData& data, ++ const util::ParamData& /* data */, + const typename boost::disable_if>::type* /* junk */, + const typename boost::enable_if>::type* /* junk */) + { +- // Get the filename and return it, or return an empty string. +- typedef std::tuple TupleType; +- const TupleType& tuple = *boost::any_cast(&data.value); +- const std::string& filename = std::get<1>(tuple); +- return "'" + filename + "'"; ++ return "''"; + } + + +diff --git a/src/mlpack/bindings/cli/end_program.hpp b/src/mlpack/bindings/cli/end_program.hpp +index 031c49d5f..8e412db10 100644 +--- a/src/mlpack/bindings/cli/end_program.hpp ++++ b/src/mlpack/bindings/cli/end_program.hpp +@@ -50,7 +50,7 @@ inline void EndProgram() + while (it != parameters.end()) + { + // Now, figure out what type it is, and print it. +- // We can handle strings, ints, bools, floats, doubles. ++ // We can handle strings, ints, bools, doubles. + const util::ParamData& data = it->second; + std::string boostName; + CLI::GetSingleton().functionMap[data.tname]["MapParameterName"](data, +diff --git a/src/mlpack/bindings/cli/get_printable_type.hpp b/src/mlpack/bindings/cli/get_printable_type.hpp +new file mode 100644 +index 000000000..c4634c4ad +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_HPP ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/get_printable_type_impl.hpp b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..bb1ff85a5 +--- /dev/null ++++ b/src/mlpack/bindings/cli/get_printable_type_impl.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of a parameter. This type is not the C++ type but ++ * instead the command-line type that a user would use. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ if (std::is_same::value) ++ return "flag"; ++ else if (std::is_same::value) ++ return "int"; ++ else if (std::is_same::value) ++ return "double"; ++ else if (std::is_same::value) ++ return "string"; ++ else ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ return "int vector"; ++ else if (std::is_same>::value) ++ return "string vector"; ++ else ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ return "2-d matrix file"; ++ else if (std::is_same>::value) ++ return "2-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else if (std::is_same::value) ++ return "1-d matrix file"; ++ else if (std::is_same>::value) ++ return "1-d index matrix file"; ++ else ++ throw std::invalid_argument("unknown Armadillo type" + data.cppType); ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "2-d categorical matrix file"; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string GetPrintableType( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return data.cppType + " file"; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_doc_functions.hpp b/src/mlpack/bindings/cli/print_doc_functions.hpp +index cfc024b2c..81ce73816 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions.hpp +@@ -20,12 +20,38 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ ++/** ++ * Print documentation for each of the types. ++ */ ++inline std::string PrintTypeDocs(); ++ + /** + * Given a parameter type, print the corresponding value. + */ + template + inline std::string PrintValue(const T& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -36,6 +62,12 @@ inline std::string PrintDataset(const std::string& dataset); + */ + inline std::string PrintModel(const std::string& model); + ++/** ++ * Print the type of a parameter that a user would specify from the ++ * command-line. ++ */ ++inline std::string PrintType(const util::ParamData& param); ++ + /** + * Base case for recursion. + */ +@@ -56,6 +88,12 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +index 0f37e7d5e..96349f5dd 100644 +--- a/src/mlpack/bindings/cli/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/cli/print_doc_functions_impl.hpp +@@ -20,6 +20,31 @@ namespace mlpack { + namespace bindings { + namespace cli { + ++/** ++ * Given the name of a binding, print its command-line name (this returns ++ * "mlpack_". ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ return "mlpack_" + bindingName; ++} ++ ++/** ++ * Print any imports for CLI (there are none, so this returns an empty string). ++ */ ++inline std::string PrintImport(const std::string& /* bindingName */) ++{ ++ return ""; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return ""; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -35,6 +60,23 @@ inline std::string PrintValue(const T& value, bool quotes) + return oss.str(); + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + /** + * Print a dataset type parameter (add .csv and return). + */ +@@ -107,10 +149,76 @@ std::string ProcessOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args) + { +- return util::HyphenateString("$ " + programName + " " + ++ return util::HyphenateString("$ " + GetBindingName(programName) + " " + + ProcessOptions(args...), 2); + } + ++/** ++ * Given a program name, print a program call invocation assuming that all ++ * options are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << "$ " << GetBindingName(programName); ++ ++ // Handle all options---first input options, then output options. ++ const std::map& parameters = CLI::Parameters(); ++ ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " "; ++ if (!it->second.required) ++ oss << "["; ++ ++ oss << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ ++ if (!it->second.required) ++ oss << "]"; ++ } ++ ++ // Now get the output options. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Otherwise, print the name and the default value. ++ std::string name; ++ CLI::GetSingleton().functionMap[it->second.tname]["GetPrintableParamName"]( ++ it->second, NULL, (void*) &name); ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ if (value == "''") ++ value = ""; ++ ++ oss << " [" << name; ++ if (it->second.cppType != "bool") ++ oss << " " << value; ++ oss << "]"; ++ } ++ ++ return util::HyphenateString(oss.str(), 8); ++} ++ + /** + * Print what a user would type to invoke the given option name. Note that the + * name *must* exist in the CLI module. (Note that because of the way +diff --git a/src/mlpack/bindings/cli/print_help.cpp b/src/mlpack/bindings/cli/print_help.cpp +index c6ef6c90d..ff62a7154 100644 +--- a/src/mlpack/bindings/cli/print_help.cpp ++++ b/src/mlpack/bindings/cli/print_help.cpp +@@ -118,7 +118,11 @@ void PrintHelp(const std::string& param) + } + + // Append default value to description. +- if (pass >= 1 && data.cppType != "bool") ++ if (pass >= 1 && (data.cppType == "int" || data.cppType == "double" || ++ data.cppType == "std::string" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector" || ++ data.cppType == "std::vector")) + { + std::string defaultValue; + CLI::GetSingleton().functionMap[data.tname]["DefaultParam"](data, +diff --git a/src/mlpack/bindings/cli/print_type_doc.hpp b/src/mlpack/bindings/cli/print_type_doc.hpp +new file mode 100644 +index 000000000..677854e60 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/cli/print_type_doc_impl.hpp b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..3a5a20912 +--- /dev/null ++++ b/src/mlpack/bindings/cli/print_type_doc_impl.hpp +@@ -0,0 +1,173 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_CLI_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace cli { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option. If not specified, it is false; if " ++ "specified, it is true."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A vector of integers, separated by commas (i.e., \"1,2,3\")."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A vector of strings, separated by commas (i.e., " ++ "\"hello\",\"goodbye\")."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type" + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ return "A data matrix filename. The file can be CSV (.csv), TSV (.csv), " ++ "ASCII (space-separated values, .txt), Armadillo ASCII (.txt), PGM " ++ "(.pgm), PPM (.ppm), Armadillo binary (.bin), or HDF5 (.h5, .hdf, " ++ ".hdf5, or .he5), if mlpack was compiled with HDF5 support. The type " ++ "of the data is detected by the extension of the filename. The storage" ++ " should be such that one row corresponds to one point, and one column " ++ "corresponds to one dimension (this is the typical storage format for " ++ "on-disk data). All values of the matrix will be loaded as double-" ++ "precision floating point data."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A data matrix filename, where the matrix holds only non-negative " ++ "integer values. This type is often used for labels or indices. The " ++ "file can be CSV (.csv), TSV (.csv), ASCII (space-separated values, " ++ ".txt), Armadillo ASCII (.txt), PGM (.pgm), PPM (.ppm), Armadillo " ++ "binary (.bin), or HDF5 (.h5, .hdf, .hdf5, or .he5), if mlpack was " ++ "compiled with HDF5 support. The type of the data is detected by the " ++ "extension of the filename. The storage should be such that one row " ++ "corresponds to one point, and one column corresponds to one dimension " ++ "(this is the typical storage format for on-disk data). All values of " ++ "the matrix will be loaded as unsigned integers."; ++ } ++ else if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "A one-dimensional vector filename. This file can take the same " ++ "formats as the data matrix filenames; however, it must either contain " ++ "one row and many columns, or one column and many rows."; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "A one-dimensional vector filename, where the matrix holds only non-" ++ "negative integer values. This type is typically used for labels or " ++ "predictions or other indices. This file can take the same formats as " ++ "the data matrix filenames; however, it must either contain one row and" ++ " many columns, or one column and many rows."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A filename for a data matrix that can contain categorical " ++ "(non-numeric) data. If the file contains only numeric data, then the " ++ "same formats for regular data matrices can be used. If the file " ++ "contains strings or other values that can't be parsed as numbers, then " ++ "the type to be loaded must be CSV (.csv) or ARFF (.arff). Any non-" ++ "numeric data will be converted to an unsigned integer value, and " ++ "dimensions where the data is converted will be treated as categorical " ++ "dimensions. When using this format, there is no need for one-hot " ++ "encoding of categorical data."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "A filename containing an mlpack model. These can have one of three " ++ "formats: binary (.bin), text (.txt), and XML (.xml). The XML format " ++ "produces the largest (but most human-readable) files, while the binary " ++ "format can be significantly more compact and quicker to load and save."; ++} ++ ++} // namespace cli ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/cli/string_type_param.hpp b/src/mlpack/bindings/cli/string_type_param.hpp +index 28caa8991..702fb4668 100644 +--- a/src/mlpack/bindings/cli/string_type_param.hpp ++++ b/src/mlpack/bindings/cli/string_type_param.hpp +@@ -74,12 +74,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + const void* /* input */, + void* output); + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output); +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/cli/string_type_param_impl.hpp b/src/mlpack/bindings/cli/string_type_param_impl.hpp +index 01065086e..86fb96302 100644 +--- a/src/mlpack/bindings/cli/string_type_param_impl.hpp ++++ b/src/mlpack/bindings/cli/string_type_param_impl.hpp +@@ -80,16 +80,6 @@ inline void StringTypeParam(const util::ParamData& /* data */, + *outstr = "string"; + } + +-//! Return "float". +-template<> +-inline void StringTypeParam(const util::ParamData& /* data */, +- const void* /* input */, +- void* output) +-{ +- std::string* outstr = (std::string*) output; +- *outstr = "float"; +-} +- + //! Return "double". + template<> + inline void StringTypeParam(const util::ParamData& /* data */, +diff --git a/src/mlpack/bindings/markdown/CMakeLists.txt b/src/mlpack/bindings/markdown/CMakeLists.txt +new file mode 100644 +index 000000000..b6b31cfba +--- /dev/null ++++ b/src/mlpack/bindings/markdown/CMakeLists.txt +@@ -0,0 +1,209 @@ ++macro (not_found_return message) ++ message(STATUS "${message}") ++ ++ macro (add_markdown_docs name languages category) ++ # Do nothing. ++ endmacro() ++ ++ function (post_markdown_setup) ++ # Do nothing. ++ endfunction () ++ ++ return () ++endmacro () ++ ++if (NOT BUILD_MARKDOWN_BINDINGS) ++ not_found_return("Not building Markdown bindings.") ++endif () ++ ++# We don't need to find any libraries or anything to generate markdown ++# documentation. ++ ++# Get categories. The list of allowable categories for add_markdown_docs() is ++# in that file. ++include(MarkdownCategories.cmake) ++set(MARKDOWN_CATEGORIES ${MARKDOWN_CATEGORIES} PARENT_SCOPE) ++ ++# Add sources for Markdown bindings. ++set(SOURCES ++ "binding_info.hpp" ++ "binding_info.cpp" ++ "default_param.hpp" ++ "get_binding_name.hpp" ++ "get_binding_name.cpp" ++ "get_param.hpp" ++ "get_printable_param.hpp" ++ "get_printable_param_name.hpp" ++ "get_printable_param_name_impl.hpp" ++ "get_printable_type.hpp" ++ "md_option.hpp" ++ "print_doc_functions.hpp" ++ "print_doc_functions_impl.hpp" ++ "print_docs.hpp" ++ "print_docs.cpp" ++ "print_type_doc.hpp" ++ "program_doc_wrapper.hpp" ++) ++ ++# Copy all Markdown sources to the build directory. ++add_custom_target(markdown_copy) ++add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++foreach(file ${SOURCES}) ++ add_custom_command(TARGET markdown_copy PRE_BUILD ++ COMMAND ${CMAKE_COMMAND} ARGS -E copy ++ ${CMAKE_CURRENT_SOURCE_DIR}/${file} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/) ++endforeach() ++ ++# Create the add_markdown_docs() macro. It's meant to be used as ++# 'add_markdown_docs(knn, "cli;python;julia", "classification")', for instance. ++# See the file 'MarkdownCategories.cmake' for valid categories that can be used ++# by the macro. ++macro (add_markdown_docs name languages category) ++ ++ # First, make sure that the category is a valid category. ++ list(FIND MARKDOWN_CATEGORIES ${category} cat_index) ++ if (${cat_index} EQUAL -1) ++ string(CONCAT error_str "add_markdown_docs(): unknown category ${category}!" ++ " See the categories in " ++ "src/mlpack/bindings/markdown/MarkdownCategories.cmake.") ++ message(FATAL_ERROR "${ERROR_STR}") ++ endif () ++ ++ # Next, we should use configure_file() to generate each ++ # generate_markdown..cpp. We need to loop over all the languages for ++ # this binding to do that. ++ set(BINDING ${name}) ++ set(LANGUAGES_PUSH_BACK_CODE "") ++ set(PROGRAM_MAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${name}_main.cpp") ++ foreach (lang ${languages}) ++ set(LANGUAGES_PUSH_BACK_CODE ++ "${LANGUAGES_PUSH_BACK_CODE}\n languages.push_back(\"${lang}\");") ++ set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} ${lang}) ++ endforeach () ++ list(REMOVE_DUPLICATES MARKDOWN_ALL_LANGUAGES_LIST) ++ ++ # Do the actual file configuration. ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.hpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.hpp) ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.binding.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.${name}.cpp) ++ ++ # Lastly, that generate_markdown..cpp should be added to the list of ++ # files to be compiled for the 'generate_markdown' target. We also need to ++ # add information about this binding to a set of variables we have to track. ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.hpp ++ ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown/generate_markdown.${name}.cpp) ++ set (MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} ${name}) ++ set (MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} ${category}) ++ set (MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++ set (MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) ++endmacro () ++ ++# After all the methods/ directories have been traversed, we can add the ++# 'generate_markdown' target. This function is run at the bottom of ++# methods/CMakeLists.txt. ++function (post_markdown_setup) ++ # We need to generate the program file. This consists of generating three ++ # things: ++ # ++ # - MARKDOWN_INCLUDES: the list of files to be included. ++ # - MARKDOWN_HEADER_CODE: the code used to print the header/sidebar. ++ # - MARKDOWN_CALL_CODE: the code to actually call the functions to print ++ # documentation for each binding. ++ ++ # Iterate over categories of binding. ++ list(LENGTH MARKDOWN_NAMES NUM_MARKDOWN_BINDINGS) ++ list(LENGTH MARKDOWN_CATEGORIES NUM_MARKDOWN_CATEGORIES) ++ math(EXPR cat_limit "${NUM_MARKDOWN_CATEGORIES} - 1") ++ foreach (i RANGE ${cat_limit}) ++ list (GET MARKDOWN_CATEGORIES ${i} cat) ++ # Put the things in this category in a div. ++ string(CONCAT header_code "${MARKDOWN_HEADER_CODE} cout << " ++ "\"
    \" << endl;\n " ++ "cout << \"
    ${cat}:
    \" << endl;\n") ++ set(MARKDOWN_HEADER_CODE ${header_code}) ++ ++ # Add an option for this binding. ++ math(EXPR range_limit "${NUM_MARKDOWN_BINDINGS} - 1") ++ foreach (j RANGE ${range_limit}) ++ list (GET MARKDOWN_NAME_CATEGORIES ${j} category) ++ if (NOT category STREQUAL cat) ++ continue () ++ endif () ++ ++ list (GET MARKDOWN_NAMES ${j} name) ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n Print${name}Headers();") ++ endforeach () ++ ++ set (MARKDOWN_HEADER_CODE ++ "${MARKDOWN_HEADER_CODE}\n cout << \"
    \" << endl << endl;") ++ endforeach () ++ ++ foreach (name ${MARKDOWN_NAMES}) ++ set (MARKDOWN_INCLUDE_CODE ++ "${MARKDOWN_INCLUDE_CODE}\n#include \"generate_markdown.${name}.hpp\"") ++ set (MARKDOWN_CALL_CODE "${MARKDOWN_CALL_CODE}\n Print${name}Docs();") ++ endforeach () ++ ++ set(BINDING_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/markdown) ++ set(BINDING_BINARY_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/markdown) ++ ++ configure_file(${BINDING_SOURCE_DIR}/generate_markdown.cpp.in ++ ${BINDING_BINARY_DIR}/generate_markdown.cpp) ++ ++ # Remember that this is being run from some other directory, so we have to be ++ # explicit with the locations of the files we are compiling against. ++ add_executable(generate_markdown ++ "${BINDING_BINARY_DIR}/generate_markdown.cpp" ++ ${MARKDOWN_SRCS} ++ "${BINDING_SOURCE_DIR}/binding_info.hpp" ++ "${BINDING_SOURCE_DIR}/binding_info.cpp" ++ "${BINDING_SOURCE_DIR}/default_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_binding_name.cpp" ++ "${BINDING_SOURCE_DIR}/get_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name.hpp" ++ "${BINDING_SOURCE_DIR}/get_printable_param_name_impl.hpp" ++ "${BINDING_SOURCE_DIR}/md_option.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions.hpp" ++ "${BINDING_SOURCE_DIR}/print_doc_functions_impl.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.hpp" ++ "${BINDING_SOURCE_DIR}/print_docs.cpp" ++ "${BINDING_SOURCE_DIR}/program_doc_wrapper.hpp" ++ "${BINDING_SOURCE_DIR}/generate_markdown.cpp") ++ target_link_libraries(generate_markdown mlpack ${MLPACK_LIBRARIES}) ++ add_dependencies(generate_markdown markdown_copy) ++ set_target_properties(generate_markdown PROPERTIES ++ COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_MARKDOWN ++ RUNTIME_OUTPUT_DIRECTORY ${BINDING_BINARY_DIR}) ++ ++ add_custom_target(markdown ALL ++ ${CMAKE_COMMAND} -E make_directory ++ ${CMAKE_BINARY_DIR}/doc/ ++ COMMAND ${CMAKE_COMMAND} ++ -DPROGRAM=${BINDING_BINARY_DIR}/generate_markdown ++ -DOUTPUT_FILE=${CMAKE_BINARY_DIR}/doc/mlpack.md ++ -P ${CMAKE_SOURCE_DIR}/CMake/RunProgram.cmake ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/change_language.js ++ ${CMAKE_BINARY_DIR}/doc/res/change_languages.js ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/menu_bg.png ++ ${CMAKE_BINARY_DIR}/doc/res/menu_bg.png ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${BINDING_SOURCE_DIR}/res/formatting.css ++ ${CMAKE_BINARY_DIR}/doc/res/formatting.css ++ DEPENDS generate_markdown ++ COMMENT "Generating Markdown documentation for mlpack bindings...") ++endfunction () +diff --git a/src/mlpack/bindings/markdown/MarkdownCategories.cmake b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +new file mode 100644 +index 000000000..6b8d697cf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/MarkdownCategories.cmake +@@ -0,0 +1,11 @@ ++# This is a list of categories of binding that Markdown can handle. If the ++# category you choose for a Markdown binding is not in this list, an error will ++# be thrown. ++set (MARKDOWN_CATEGORIES ++ "classification" ++ "regression" ++ "clustering" ++ "geometry" ++ "preprocessing" ++ "misc. / other" ++ "transformations") +diff --git a/src/mlpack/bindings/markdown/binding_info.cpp b/src/mlpack/bindings/markdown/binding_info.cpp +new file mode 100644 +index 000000000..0a61103d3 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.cpp +@@ -0,0 +1,49 @@ ++/** ++ * @file binding_info.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of BindingInfo functions. ++ */ ++#include "binding_info.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++util::ProgramDoc& BindingInfo::GetProgramDoc(const std::string& bindingName) ++{ ++ if (GetSingleton().map.count(bindingName) == 0) ++ { ++ throw std::invalid_argument("Binding name '" + bindingName + ++ "' not known!"); ++ } ++ ++ return GetSingleton().map.at(bindingName); ++} ++ ++/** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++void BindingInfo::RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc) ++{ ++ GetSingleton().map[bindingName] = programDoc; ++} ++ ++//! Get or modify the current language (don't set it to something invalid!). ++std::string& BindingInfo::Language() ++{ ++ return GetSingleton().language; ++} ++ ++BindingInfo& BindingInfo::GetSingleton() ++{ ++ static BindingInfo instance; ++ return instance; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/binding_info.hpp b/src/mlpack/bindings/markdown/binding_info.hpp +new file mode 100644 +index 000000000..6a99d2ebc +--- /dev/null ++++ b/src/mlpack/bindings/markdown/binding_info.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file binding_name.hpp ++ * @author Ryan Curtin ++ * ++ * This file defines the BindingInfo singleton class that is used specifically ++ * for the Markdown bindings to map from a binding name (i.e. "knn") to ++ * multiple ProgramDoc objects, which are then used to generate the ++ * documentation. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_BINDING_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The BindingInfo class is used by the Markdown documentation generator to ++ * store multiple ProgramDoc objects, indexed by both the binding name (i.e. ++ * "knn") and the language (i.e. "cli"). ++ */ ++class BindingInfo ++{ ++ public: ++ /** ++ * Return a ProgramDoc object for a given bindingName. ++ */ ++ static util::ProgramDoc& GetProgramDoc(const std::string& bindingName); ++ ++ /** ++ * Register a ProgramDoc object with the given bindingName. ++ */ ++ static void RegisterProgramDoc(const std::string& bindingName, ++ const util::ProgramDoc& programDoc); ++ ++ //! Get or modify the current language (don't set it to something invalid!). ++ static std::string& Language(); ++ ++ private: ++ //! Private constructor, so that only one instance can be created. ++ BindingInfo() { } ++ ++ //! Get the singleton. ++ static BindingInfo& GetSingleton(); ++ ++ //! Internally-held map for mapping a binding name to a ProgramDoc name. ++ std::unordered_map map; ++ ++ //! Holds the name of the language that we are currently printing. This is ++ //! modified before printing the documentation, and then used by ++ //! print_doc_functions.hpp during printing to print the correct language. ++ std::string language; ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/default_param.hpp b/src/mlpack/bindings/markdown/default_param.hpp +new file mode 100644 +index 000000000..78af807e0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/default_param.hpp +@@ -0,0 +1,45 @@ ++/** ++ * @file default_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get the default value of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_DEFAULT_PARAM_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the default value of a parameter into the output string. The type ++ * printed depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::DefaultParamImpl::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::DefaultParamImpl::type>(data); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +new file mode 100644 +index 000000000..18a07e5ec +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.cpp.in +@@ -0,0 +1,46 @@ ++/** ++ * @file generate_markdown.binding.cpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#define BINDING_NAME "${BINDING}" ++ ++#include ++#include "generate_markdown.${BINDING}.hpp" ++#include "binding_info.hpp" ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++static const std::string testName = "${BINDING}"; ++#include <${PROGRAM_MAIN_FILE}> ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintHeaders("${BINDING}", languages); ++} ++ ++void Print${BINDING}Docs() ++{ ++ // Fill the vector of languages for which we want to print. ++ vector languages; ++ ${LANGUAGES_PUSH_BACK_CODE} ++ ++ PrintDocs("${BINDING}", languages); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +new file mode 100644 +index 000000000..3e81c9c3a +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.binding.hpp.in +@@ -0,0 +1,28 @@ ++/** ++ * @file generate_markdown.binding.hpp.in ++ * @author Ryan Curtin ++ * ++ * Print Markdown for a specific binding. This provides two utility ++ * methods---one that prints info for a table of contents, and one that prints ++ * the Markdown bindings themselves. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GENERATE_MARKDOWN_${BINDING}_HPP ++ ++#include "print_docs.hpp" ++#include "get_binding_name.hpp" ++ ++using namespace std; ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++void Print${BINDING}Headers(); ++void Print${BINDING}Docs(); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/generate_markdown.cpp.in b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +new file mode 100644 +index 000000000..abacae377 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/generate_markdown.cpp.in +@@ -0,0 +1,114 @@ ++/** ++ * @file generate_markdown.cpp.in ++ * @author Ryan Curtin ++ * ++ * This file is configured by CMake to generate all of the Markdown required by ++ * the project. ++ */ ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++${MARKDOWN_INCLUDE_CODE} ++ ++using namespace mlpack; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++using namespace std; ++ ++int main() ++{ ++ // These come to use from CMake separated by semicolons. ++ string languageList = "${MARKDOWN_ALL_LANGUAGES_LIST}"; ++ vector languages; ++ size_t index; ++ while ((index = languageList.find(';')) != string::npos) ++ { ++ languages.push_back(languageList.substr(0, index)); ++ languageList = languageList.substr(index + 1); ++ } ++ languages.push_back(languageList); ++ ++ cout << "
    " << endl; ++ ++ // We need to create the input form selector for the language. ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << " " << endl; ++ cout << "
    " << endl; ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // The links to the data type sections get put here. ++ cout << " - [mlpack overview](#mlpack-overview){: .language-link #always }" ++ << endl; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << " - [data formats](#" << languages[i] << "_data-formats){: " ++ << ".language-link #" << languages[i] << " }" << endl; ++ } ++ ++ ${MARKDOWN_HEADER_CODE} ++ ++ cout << endl << "
    " << endl << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ ++ // Create all the headers for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "
    " << endl; ++ cout << "# " << util::GetVersion() << " " << PrintLanguage(languages[i]) ++ << " binding documentation" << endl; ++ cout << "
    " << endl; ++ } ++ ++ /** ++ * "mlpack overview" section. This will go at the top of the page. ++ */ ++ cout << "## mlpack overview" << endl; ++ cout << endl; ++ cout << "mlpack is an intuitive, fast, and flexible C++ machine learning " ++ "library with bindings to other languages. It is meant to be a machine " ++ "learning analog to LAPACK, and aims to implement a wide array of machine" ++ " learning methods and functions as a \"swiss army knife\" for machine " ++ "learning researchers."; ++ cout << endl << endl; ++ cout << "This reference page details mlpack's bindings to other languages. " ++ "Further useful mlpack documentation links are given below."; ++ cout << endl << endl; ++ cout << " - [mlpack homepage](https://www.mlpack.org/)" << endl; ++ cout << " - [mlpack on Github](https://github.com/mlpack/mlpack)" << endl; ++ cout << " - [mlpack main documentation page]" ++ << "(https://www.mlpack.org/docs.html)" << endl; ++ cout << endl; ++ ++ /** ++ * Discussion of different data types section. This goes just below the ++ * overview section at the top of the page. ++ */ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ cout << PrintTypeDocs() << endl; ++ } ++ ++ ${MARKDOWN_CALL_CODE} ++ cout << "
    " << endl << endl; ++ ++ // Make sure script gets included for changeLanguage(). ++ cout << "" << endl; ++} +diff --git a/src/mlpack/bindings/markdown/get_binding_name.cpp b/src/mlpack/bindings/markdown/get_binding_name.cpp +new file mode 100644 +index 000000000..67d229b6c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.cpp +@@ -0,0 +1,40 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#include "get_binding_name.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++std::string GetBindingName(const std::string& language, ++ const std::string& name) ++{ ++ // Unfortunately, every time a new binding is added, this code will need to be ++ // modified. ++ if (language == "cli") ++ { ++ // For command-line programs, all bindings have 'mlpack_' prepended to the ++ // name. ++ return "mlpack_" + name; ++ } ++ else if (language == "python") ++ { ++ // For Python bindings, the name is unchanged. ++ return name; ++ } ++ else ++ { ++ throw std::invalid_argument("Don't know how to compute binding name for " ++ "language \"" + language + "\"! Is the language specified in " ++ "src/mlpack/bindings/markdown/get_binding_name.cpp?"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack +diff --git a/src/mlpack/bindings/markdown/get_binding_name.hpp b/src/mlpack/bindings/markdown/get_binding_name.hpp +new file mode 100644 +index 000000000..21c55f05c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_binding_name.hpp +@@ -0,0 +1,30 @@ ++/** ++ * @file get_binding_name.cpp ++ * @author Ryan Curtin ++ * ++ * Given the name of a binding as it appears in CMake, return the corresponding ++ * name of the binding that is generated for a given language. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_BINDING_NAME_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given a language name and a binding name, return the name of that binding for ++ * that language. Note that if a new language is added to the mlpack bindings, ++ * this method will need to be updated so that documentation can be successfully ++ * generated for that language. ++ */ ++std::string GetBindingName(const std::string& language, ++ const std::string& name); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_param.hpp b/src/mlpack/bindings/markdown/get_param.hpp +new file mode 100644 +index 000000000..6c1611f49 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_param.hpp +@@ -0,0 +1,38 @@ ++/** ++ * @file get_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a parameter for a Markdown binding. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PARAM_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * All Markdown binding types are exactly what is held in the ParamData, so no ++ * special handling is necessary. ++ */ ++template ++void GetParam(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ util::ParamData& dmod = const_cast(d); ++ *((T**) output) = boost::any_cast(&dmod.value); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param.hpp b/src/mlpack/bindings/markdown/get_printable_param.hpp +new file mode 100644 +index 000000000..53a862b61 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param.hpp +@@ -0,0 +1,126 @@ ++/** ++ * @file get_printable_param.hpp ++ * @author Ryan Curtin ++ * ++ * Get a printable version of parameters. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print an option of a simple type. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a vector option, with spaces between it. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ const T& t = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ for (size_t i = 0; i < t.size(); ++i) ++ oss << t[i] << " "; ++ return oss.str(); ++} ++ ++/** ++ * Print a matrix option (this prints its size). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0) ++{ ++ // Get the matrix. ++ const T& matrix = boost::any_cast(data.value); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix"; ++ return oss.str(); ++} ++ ++/** ++ * Print a serializable class option (this prints the class name). ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0) ++{ ++ std::ostringstream oss; ++ oss << data.cppType << " model at " << boost::any_cast(data.value); ++ return oss.str(); ++} ++ ++/** ++ * Print a combination DatasetInfo/matrix parameter. ++ */ ++template ++std::string GetPrintableParam( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0) ++{ ++ // Get the matrix. ++ const T& tuple = boost::any_cast(data.value); ++ const arma::mat& matrix = std::get<1>(tuple); ++ ++ std::ostringstream oss; ++ oss << matrix.n_rows << "x" << matrix.n_cols << " matrix with dimension type " ++ << "information"; ++ return oss.str(); ++} ++ ++/** ++ * Print an option into a std::string. This should print a short, one-line ++ * representation of the object. The string will be stored in the output ++ * pointer. ++ * ++ * @param data Parameter data struct. ++ * @param input Unused parameter. ++ * @param output Output storage for the string. ++ */ ++template ++void GetPrintableParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParam::type>(data); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name.hpp b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +new file mode 100644 +index 000000000..9ceca6b4c +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name.hpp +@@ -0,0 +1,83 @@ ++/** ++ * @file get_printable_param_name.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamName( ++ const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamName::type>(d); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_name_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +new file mode 100644 +index 000000000..41b9c2972 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_name_impl.hpp +@@ -0,0 +1,79 @@ ++/** ++ * @file get_printable_param_name_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter name that the user would specify on the command line, ++ * with different behavior for different parameter types. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_NAME_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "--" + data.name; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamName( ++ const util::ParamData& data, ++ const typename boost::enable_if>>::type*) ++{ ++ return "--" + data.name + "_file"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value.hpp b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +new file mode 100644 +index 000000000..d58bc93bf +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value.hpp +@@ -0,0 +1,88 @@ ++/** ++ * @file get_printable_param_value.hpp ++ * @author Ryan Curtin ++ * ++ * Given a parameter value, print what the user might actually specify on the ++ * command line. Basically this adds ".csv" to types where data must be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& data, ++ const std::string& value, ++ const typename boost::enable_if>>::type* = 0); ++ ++/** ++ * Get the parameter's name as seen by the user. ++ */ ++template ++void GetPrintableParamValue( ++ const util::ParamData& d, ++ const void* input, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableParamValue::type>(d, ++ *((std::string*) input)); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "get_printable_param_value_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +new file mode 100644 +index 000000000..9fce0559b +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_param_value_impl.hpp +@@ -0,0 +1,84 @@ ++/** ++ * @file get_printable_param_value_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the parameter value that the user would specify on the command line ++ * depending on the type of the option. Basically this adds ".csv" to types ++ * that need to be loaded. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_PARAM_VALUE_IMPL_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Get the parameter name for a type that has no special handling. ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return input; ++} ++ ++/** ++ * Get the parameter name for a matrix type (where the user has to pass the file ++ * that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".csv"; ++} ++ ++/** ++ * Get the parameter name for a serializable model type (where the user has to ++ * pass the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return input + ".bin"; ++} ++ ++/** ++ * Get the parameter name for a mapped matrix type (where the user has to pass ++ * the file that holds the matrix). ++ */ ++template ++std::string GetPrintableParamValue( ++ const util::ParamData& /* data */, ++ const std::string& input, ++ const typename boost::enable_if>>::type*) ++{ ++ return input + ".arff"; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/get_printable_type.hpp b/src/mlpack/bindings/markdown/get_printable_type.hpp +new file mode 100644 +index 000000000..2b292b937 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/get_printable_type.hpp +@@ -0,0 +1,62 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Get the printable type of the parameter. This depends on ++ * BindingInfo::Language() to choose which language to return the type for. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_GET_PRINTABLE_TYPE_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++void GetPrintableType(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ *((std::string*) output) = ++ cli::GetPrintableType::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ *((std::string*) output) = ++ python::GetPrintableType::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("GetPrintableType(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the type of a parameter. The type printed depends on the current ++ * setting of BindingInfo::Language(). ++ */ ++template ++std::string GetPrintableType(const util::ParamData& data) ++{ ++ std::string output; ++ GetPrintableType(data, (void*) NULL, (void*) &output); ++ return output; ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/is_serializable.hpp b/src/mlpack/bindings/markdown/is_serializable.hpp +new file mode 100644 +index 000000000..b129487b9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/is_serializable.hpp +@@ -0,0 +1,63 @@ ++/** ++ * @file is_serializable.hpp ++ * @author Ryan Curtin ++ * ++ * Return a bool noting whether or not a parameter is serializable. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++#define MLPACK_BINDINGS_MARKDOWN_IS_SERIALIZABLE_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Return false, because the type is not serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::disable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return false, because even though the type is serializable, it is an ++ * Armadillo type not an mlpack model. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0) ++{ ++ return false; ++} ++ ++/** ++ * Return true, because the type is serializable. ++ */ ++template ++bool IsSerializable( ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0) ++{ ++ return true; ++} ++ ++/** ++ * Return whether or not the type is serializable. ++ */ ++template ++void IsSerializable(const util::ParamData& /* data */, ++ const void* /* input */, ++ void* output) ++{ ++ *((bool*) output) = IsSerializable::type>(); ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/md_option.hpp b/src/mlpack/bindings/markdown/md_option.hpp +new file mode 100644 +index 000000000..dc728c4e9 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/md_option.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file md_option.hpp ++ * @author Ryan Curtin ++ * ++ * The Markdown option type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++#define MLPACK_BINDINGS_MARKDOWN_MD_OPTION_HPP ++ ++#include ++#include ++#include "default_param.hpp" ++#include "get_param.hpp" ++#include "get_printable_param.hpp" ++#include "get_printable_param_name.hpp" // For cli bindings. ++#include "get_printable_param_value.hpp" // For cli bindings. ++#include "get_printable_type.hpp" ++#include "is_serializable.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * The Markdown option class. ++ */ ++template ++class MDOption ++{ ++ public: ++ /** ++ * Construct an MDOption object. When constructed, it will register itself ++ * with CLI. The testName parameter is not used and added for compatibility ++ * reasons. ++ */ ++ MDOption(const T defaultValue, ++ const std::string& identifier, ++ const std::string& description, ++ const std::string& alias, ++ const std::string& cppName, ++ const bool required = false, ++ const bool input = true, ++ const bool noTranspose = false, ++ const std::string& bindingName = "") ++ { ++ // Create the ParamData object to give to CLI. ++ util::ParamData data; ++ ++ data.desc = description; ++ data.name = identifier; ++ data.tname = TYPENAME(T); ++ data.alias = alias[0]; ++ data.wasPassed = false; ++ data.noTranspose = noTranspose; ++ data.required = required; ++ data.input = input; ++ data.loaded = false; ++ // Several options from Python and CLI bindings are persistent. ++ if (identifier == "verbose" || identifier == "copy_all_inputs" || ++ identifier == "help" || identifier == "info" || identifier == "version") ++ data.persistent = true; ++ else ++ data.persistent = false; ++ data.cppType = cppName; ++ ++ // Every parameter we'll get from Markdown will have the correct type. ++ data.value = boost::any(defaultValue); ++ ++ // Restore the parameters for this program. ++ if (identifier != "verbose" && identifier != "copy_all_inputs") ++ CLI::RestoreSettings(bindingName, false); ++ ++ // Set the function pointers that we'll need. Most of these simply delegate ++ // to the current binding type's implementation. Any new language will need ++ // to have all of these implemented, and the Markdown implementation will ++ // need to properly delegate. ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetParam"] = &GetParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = ++ &GetPrintableParam; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamName"] = ++ &GetPrintableParamName; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableParamValue"] = ++ &GetPrintableParamValue; ++ CLI::GetSingleton().functionMap[data.tname]["GetPrintableType"] = ++ &GetPrintableType; ++ CLI::GetSingleton().functionMap[data.tname]["IsSerializable"] = ++ &IsSerializable; ++ ++ // Add the option. ++ CLI::Add(std::move(data)); ++ if (identifier != "verbose" && identifier != "copy_all_inputs" && ++ identifier != "help" && identifier != "info" && identifier != "version") ++ CLI::StoreSettings(bindingName); ++ CLI::ClearSettings(); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions.hpp b/src/mlpack/bindings/markdown/print_doc_functions.hpp +new file mode 100644 +index 000000000..2430fa512 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions.hpp +@@ -0,0 +1,109 @@ ++/** ++ * @file print_doc_functions.hpp ++ * @author Ryan Curtin ++ * ++ * This file wraps the different printing functionality of different binding ++ * types. If a new binding type is added, this code will need to be modified so ++ * that Markdown can be printed. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language); ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(const std::string& language); ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs(); ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes); ++ ++/** ++ * Print the default value of an option, unless it is required (in which case ++ * Markdown italicized '--' is printed). ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset); ++ ++/** ++ * Print a model type parameter (add .bin and return). ++ */ ++inline std::string PrintModel(const std::string& model); ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args); ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName); ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d); ++ ++/** ++ * Return whether or not a runtime check on parameters should be ignored. ++ */ ++template ++inline bool IgnoreCheck(const T& t); ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "print_doc_functions_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +new file mode 100644 +index 000000000..13359441d +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_doc_functions_impl.hpp +@@ -0,0 +1,540 @@ ++/** ++ * @file print_doc_functions_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Call out to different printing functionality for different binding languages. ++ * If a new binding is added, this code must be modified. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOC_FUNCTIONS_IMPL_HPP ++ ++#include "print_doc_functions.hpp" ++#include "binding_info.hpp" ++#include "print_type_doc.hpp" ++#include "get_printable_type.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Given the name of the binding, print the name for the current language (as ++ * given by BindingInfo). ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::GetBindingName(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::GetBindingName(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print the name of the given language. ++ */ ++inline std::string PrintLanguage(const std::string& language) ++{ ++ if (language == "cli") ++ { ++ return "CLI"; ++ } ++ else if (language == "python") ++ { ++ return "Python"; ++ } ++ else ++ { ++ throw std::invalid_argument("PrintLanguage(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any imports that need to be done before using the binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintImport(bindingName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintImport(bindingName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintImport(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintOutputOptionInfo(); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintOutputOptionInfo(); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintOutputOptionInfo(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++namespace priv { ++ ++// We'll need a fake class for printing model type documentation. ++class mlpackModel ++{ ++ public: ++ // Fake serialization to make SFINAE work right for this type. ++ template ++ void serialize(Archive&, const unsigned int) {} ++}; ++ ++} // namespace priv ++ ++// Utility function that returns the first word (as delimited by spaces) of a ++// string. ++inline std::string ToUnderscores(const std::string& str) ++{ ++ std::string ret(str); ++ std::replace(ret.begin(), ret.end(), ' ', '_'); ++ return ret; ++} ++ ++/** ++ * Print details about the different types for a language. ++ */ ++inline std::string PrintTypeDocs() ++{ ++ std::ostringstream oss; ++ oss << "
    " << std::endl; ++ oss << "## data formats" << std::endl; ++ oss << "{: .language-types-h2 #" << BindingInfo::Language() ++ << "_data-formats }" << std::endl; ++ oss << std::endl; ++ ++ // Iterate through each of the types that we care about. ++ oss << "mlpack bindings for " << PrintLanguage(BindingInfo::Language()) ++ << " take and return a restricted set of types, for simplicity. These " ++ << "include primitive types, matrix/vector types, categorical matrix " ++ << "types, and model types. Each type is detailed below." << std::endl; ++ oss << std::endl; ++ ++ // Create fake ParamData to pass around. ++ util::ParamData data; ++ data.desc = "fake"; ++ data.name = "fake"; ++ data.tname = std::string(typeid(int).name()); ++ data.cppType = "int"; ++ data.alias = 'f'; ++ data.wasPassed = false; ++ data.noTranspose = true; ++ data.required = false; ++ data.input = true; ++ data.loaded = false; ++ data.persistent = false; ++ data.value = boost::any(int(0)); ++ ++ std::string type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(double).name()); ++ data.cppType = "double"; ++ data.value = boost::any(double(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(bool).name()); ++ data.cppType = "double"; ++ data.value = boost::any(bool(0.0)); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) << std::endl; ++ ++ data.tname = std::string(typeid(std::string).name()); ++ data.cppType = "std::string"; ++ data.value = boost::any(std::string("")); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(std::vector).name()); ++ data.cppType = "std::vector"; ++ data.value = boost::any(std::vector()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: " << "#doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ ++ data.tname = std::string(typeid(arma::mat).name()); ++ data.cppType = "arma::mat"; ++ data.value = boost::any(arma::mat()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Mat).name()); ++ data.cppType = "arma::Mat"; ++ data.value = boost::any(arma::Mat()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::rowvec).name()); ++ data.cppType = "arma::rowvec"; ++ data.value = boost::any(arma::rowvec()); ++ const std::string& rowType = GetPrintableType(data); ++ ++ oss << " - `" << rowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(rowType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::Row).name()); ++ data.cppType = "arma::Row"; ++ data.value = boost::any(arma::Row()); ++ const std::string& urowType = GetPrintableType>(data); ++ ++ oss << " - `" << urowType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(urowType) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(arma::vec).name()); ++ data.cppType = "arma::vec"; ++ data.value = boost::any(arma::vec()); ++ const std::string& colType = GetPrintableType(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (colType != rowType) ++ { ++ oss << " - `" << colType << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(colType) << " }: " << PrintTypeDoc(data) ++ << std::endl; ++ } ++ ++ data.tname = std::string(typeid(arma::Col).name()); ++ data.cppType = "arma::Col"; ++ data.value = boost::any(arma::Col()); ++ const std::string& ucolType = GetPrintableType>(data); ++ ++ // For some languages there is no distinction between column and row vectors. ++ // If that is the case, then don't print both. ++ if (ucolType != urowType) ++ { ++ oss << " - `" << ucolType << "`{ #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(ucolType) << " }: " ++ << PrintTypeDoc>(data) << std::endl; ++ } ++ ++ data.tname = ++ std::string(typeid(std::tuple).name()); ++ data.cppType = "std::tuple"; ++ data.value = boost::any(std::tuple()); ++ ++ type = GetPrintableType>(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() << "_" ++ << ToUnderscores(type) << " }: " ++ << PrintTypeDoc>(data) ++ << std::endl; ++ ++ data.tname = std::string(typeid(priv::mlpackModel).name()); ++ data.cppType = "mlpackModel"; ++ data.value = boost::any(new priv::mlpackModel()); ++ ++ type = GetPrintableType(data); ++ oss << " - `" << type << "`{: #doc_" << BindingInfo::Language() ++ << "_model }: " << PrintTypeDoc(data) << std::endl; ++ ++ // Clean up memory. ++ delete boost::any_cast(data.value); ++ ++ oss << std::endl << "
    " << std::endl; ++ ++ return oss.str(); ++} ++ ++/** ++ * Given a parameter type, print the corresponding value. ++ */ ++template ++inline std::string PrintValue(const T& value, bool quotes) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintValue(value, quotes); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintValue(value, quotes); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintValue(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter" + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::ostringstream oss; ++ ++ if (d.required) ++ { ++ oss << "**--**"; ++ } ++ else ++ { ++ if (BindingInfo::Language() == "cli") ++ { ++ oss << cli::PrintDefault(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ oss << python::PrintDefault(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDefault: unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ } ++ ++ return oss.str(); ++} ++ ++/** ++ * Print a dataset type parameter (add .csv and return). ++ */ ++inline std::string PrintDataset(const std::string& dataset) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintDataset(dataset); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintDataset(dataset); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintDataset(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Print a model type parameter. ++ */ ++inline std::string PrintModel(const std::string& model) ++{ ++ std::string result; ++ if (BindingInfo::Language() == "cli") ++ { ++ result = cli::PrintModel(model); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ result = python::PrintModel(model); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintModel(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + result + "`"; ++} ++ ++/** ++ * Given a program name and arguments for it, print what its invocation would ++ * be. ++ */ ++template ++std::string ProgramCall(const std::string& programName, Args... args) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ s += cli::ProgramCall(programName, args...); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ s += python::ProgramCall(programName, args...); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```"; ++ return s; ++} ++ ++/** ++ * Given a program name, print a call assuming that all arguments are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::string s = "```"; ++ if (BindingInfo::Language() == "cli") ++ { ++ s += "bash\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += "$ " + import + "\n"; ++ s += cli::ProgramCall(programName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s += "python\n"; ++ std::string import = PrintImport(GetBindingName(programName)); ++ if (import.size() > 0) ++ s += ">>> " + import + "\n"; ++ s += python::ProgramCall(programName); ++ } ++ else ++ { ++ throw std::invalid_argument("ProgramCall(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ s += "\n```\n"; ++ return s; ++} ++ ++/** ++ * Print what a user would type to invoke the given option name. Note that the ++ * name *must* exist in the CLI module. (Note that because of the way ++ * ProgramInfo is structured, this doesn't mean that all of the PARAM_*() ++ * declarataions need to come before the PROGRAM_INFO() declaration.) ++ */ ++inline std::string ParamString(const std::string& paramName) ++{ ++ // These functions always put a '' around the parameter, so we will skip that ++ // bit. ++ std::string s; ++ if (BindingInfo::Language() == "cli") ++ { ++ // The CLI bindings put a '' around the parameter, so skip that... ++ s = cli::ParamString(paramName); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ s = python::ParamString(paramName); ++ } ++ else ++ { ++ throw std::invalid_argument("ParamString(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++ ++ return "`" + s.substr(1, s.size() - 2) + "`"; ++} ++ ++/** ++ * Print the user-encountered type of an option. ++ */ ++inline std::string ParamType(const util::ParamData& d) ++{ ++ std::string output; ++ CLI::GetSingleton().functionMap[d.tname]["GetPrintableType"](d, NULL, ++ &output); ++ // We want to make this a link to the type documentation. ++ std::string anchorType = output; ++ bool result; ++ CLI::GetSingleton().functionMap[d.tname]["IsSerializable"](d, NULL, &result); ++ if (result) ++ anchorType = "model"; ++ ++ return "[`" + output + "`](#doc_" + BindingInfo::Language() + "_" + ++ ToUnderscores(anchorType) + ")"; ++} ++ ++template ++inline bool IgnoreCheck(const T& t) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::IgnoreCheck(t); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::IgnoreCheck(t); ++ } ++ else ++ { ++ throw std::invalid_argument("IgnoreCheck(): unknown " ++ "BindingInfo::Language(): " + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_docs.cpp b/src/mlpack/bindings/markdown/print_docs.cpp +new file mode 100644 +index 000000000..2711a8789 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.cpp +@@ -0,0 +1,218 @@ ++/** ++ * @file print_docs.cpp ++ * @author Ryan Curtin ++ * ++ * Implementation of functions to print Markdown from documentation. ++ */ ++#include "print_docs.hpp" ++ ++#include ++#include ++#include "binding_info.hpp" ++#include "print_doc_functions.hpp" ++ ++// Make sure that this is defined. ++#ifndef DOXYGEN_PREFIX ++#define DOXYGEN_PREFIX "https://mlpack.org/docs/mlpack-git/doxygen/" ++#endif ++ ++using namespace std; ++using namespace mlpack; ++using namespace mlpack::util; ++using namespace mlpack::bindings; ++using namespace mlpack::bindings::markdown; ++ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages) ++{ ++ // We just want to print the name of the function and a link, as Markdown. ++ // We have to mark it as having the right language with a div. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << " - [" << GetBindingName(bindingName) << "](#" << languages[i] ++ << "_" << bindingName << "){: .language-link #" << languages[i] << " }" ++ << endl; ++ } ++} ++ ++void PrintDocs(const std::string& bindingName, ++ const vector& languages) ++{ ++ ProgramDoc& programDoc = BindingInfo::GetProgramDoc(bindingName); ++ ++ CLI::RestoreSettings(bindingName); ++ ++ // First, for this section, print each of the names. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << "## " << GetBindingName(bindingName) << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName << " }" << endl; ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ // Next we want to print the logical name of the binding (that's known by ++ // ProgramInfo). ++ cout << "#### " << programDoc.programName << endl; ++ cout << endl; ++ ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ cout << "
    " << endl; ++ cout << ProgramCall(bindingName); ++ cout << "
    " << endl; ++ } ++ cout << endl; ++ ++ cout << programDoc.shortDocumentation << " "; ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ cout << "[Detailed documentation](#" << languages[i] << "_" ++ << bindingName << "_detailed-documentation){: .language-detail-link #" ++ << languages[i] << " }"; ++ } ++ cout << "." << endl; ++ ++ // Next, print the PROGRAM_INFO() documentation for each language. ++ for (size_t i = 0; i < languages.size(); ++i) ++ { ++ BindingInfo::Language() = languages[i]; ++ ++ // This works with the Kramdown processor. ++ cout << "
    " << endl; ++ ++ // We need to print the signature. ++ ++ // Now, iterate through each of the input options. ++ cout << endl; ++ cout << "### Input options" << endl; ++ cout << endl; ++ ++ cout << "| ***name*** | ***type*** | ***description*** | ***default*** |" ++ << endl; ++ cout << "|------------|------------|-------------------|---------------|" ++ << endl; ++ map& parameters = CLI::Parameters(); ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ continue; ++ ++ // There are some special options that don't exist in some languages. ++ if (languages[i] != "python" && it->second.name == "copy_all_inputs") ++ continue; ++ if (languages[i] != "cli" && ++ (it->second.name == "help" || it->second.name == "info" || ++ it->second.name == "version")) ++ continue; ++ ++ // Print name, type, description, default. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; // just a string ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " | "; ++ string def = PrintDefault(it->second.name); ++ if (def.size() > 0) ++ cout << "`" << def << "` |"; ++ else ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ // Next, iterate through the list of output options. ++ cout << "### Output options" << endl; ++ cout << endl; ++ string outputInfo = PrintOutputOptionInfo(); ++ if (outputInfo.size() > 0) ++ cout << outputInfo << endl; ++ cout << endl; ++ cout << "| ***name*** | ***type*** | ***description*** |" << endl; ++ cout << "|------------|------------|-------------------|" << endl; ++ for (map::const_iterator it = parameters.begin(); ++ it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print name, type, description. ++ cout << "| "; ++ cout << ParamString(it->second.name) << " | "; ++ cout << ParamType(it->second) << " | "; ++ cout << it->second.desc; ++ // Print whether or not it's a "special" language-only parameter. ++ if (it->second.name == "copy_all_inputs" || it->second.name == "help" || ++ it->second.name == "info" || it->second.name == "version") ++ { ++ cout << " Only exists in " ++ << PrintLanguage(languages[i]) << " binding."; ++ } ++ cout << " |"; ++ cout << endl; ++ } ++ cout << endl; ++ ++ cout << "### Detailed documentation" << endl; ++ cout << "{: #" << languages[i] << "_" << bindingName ++ << "_detailed-documentation }" << endl; ++ cout << endl; ++ cout << programDoc.documentation() << endl; ++ cout << endl; ++ ++ cout << "### See also" << endl; ++ cout << endl; ++ for (size_t j = 0; j < programDoc.seeAlso.size(); ++j) ++ { ++ cout << " - " << "["; ++ // We need special processing if the user has specified a binding name ++ // starting with @ (i.e., '@kfn' or similar). ++ if (programDoc.seeAlso[j].first[0] == '@') ++ cout << GetBindingName(programDoc.seeAlso[j].first.substr(1)); ++ else ++ cout << programDoc.seeAlso[j].first; ++ cout << "]("; ++ ++ // We need special handling of Doxygen information. ++ if (programDoc.seeAlso[j].second.substr(0, 8) == "@doxygen") ++ { ++ cout << DOXYGEN_PREFIX << programDoc.seeAlso[j].second.substr(9); ++ } ++ else if (programDoc.seeAlso[j].second[0] == '#') ++ { ++ cout << "#" << languages[i] << "_" ++ << programDoc.seeAlso[j].second.substr(1); ++ } ++ else ++ { ++ cout << programDoc.seeAlso[j].second; ++ } ++ ++ cout << ")" << endl; ++ } ++ cout << endl; ++ ++ cout << "
    " << endl; ++ cout << endl; ++ } ++ ++ CLI::ClearSettings(); ++} +diff --git a/src/mlpack/bindings/markdown/print_docs.hpp b/src/mlpack/bindings/markdown/print_docs.hpp +new file mode 100644 +index 000000000..abbc88398 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_docs.hpp +@@ -0,0 +1,33 @@ ++/** ++ * @file print_docs.hpp ++ * @author Ryan Curtin ++ * ++ * Functions to generate Markdown for documentation of bindings. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_DOCS_HPP ++ ++#include ++ ++/** ++ * Given the current settings of CLI, print the header (which will be the ++ * navigation tab) for the binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ */ ++void PrintHeaders(const std::string& bindingName, ++ const std::vector& languages); ++ ++/** ++ * Given the current settings of CLI, print Markdown documentation for the ++ * binding types that are registered for these options. ++ * ++ * Output is printed to stdout. ++ * ++ * @param bindingName The binding name (${BINDING} from CMake). ++ * @param languages The set of languages to print documentation for. ++ */ ++void PrintDocs(const std::string& bindingName, ++ const std::vector& languages); ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/print_type_doc.hpp b/src/mlpack/bindings/markdown/print_type_doc.hpp +new file mode 100644 +index 000000000..108461ed5 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/print_type_doc.hpp +@@ -0,0 +1,46 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, depending on the current language (as ++ * set in BindingInfo). ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PRINT_TYPE_DOC_HPP ++ ++#include "binding_info.hpp" ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++/** ++ * Print the type of a parameter into the output string. The type printed ++ * depends on the current setting of BindingInfo::Language(). ++ */ ++template ++std::string PrintTypeDoc(const util::ParamData& data) ++{ ++ if (BindingInfo::Language() == "cli") ++ { ++ return cli::PrintTypeDoc::type>(data); ++ } ++ else if (BindingInfo::Language() == "python") ++ { ++ return python::PrintTypeDoc::type>(data); ++ } ++ else ++ { ++ throw std::invalid_argument("PrintTypeDoc(): unknown " ++ "BindingInfo::Language()" + BindingInfo::Language() + "!"); ++ } ++} ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/program_doc_wrapper.hpp b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +new file mode 100644 +index 000000000..21da3ffc0 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/program_doc_wrapper.hpp +@@ -0,0 +1,41 @@ ++/** ++ * @file program_doc_wrapper.hpp ++ * @author Ryan Curtin ++ * ++ * A simple wrapper around ProgramDoc that also calls ++ * BindingInfo::RegisterProgramDoc() upon construction. ++ */ ++#ifndef MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++#define MLPACK_BINDINGS_MARKDOWN_PROGRAM_DOC_WRAPPER_HPP ++ ++#include "binding_info.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace markdown { ++ ++class ProgramDocWrapper ++{ ++ public: ++ /** ++ * Construct a ProgramDoc object and register it with ++ * BindingInfo::RegisterProgramDoc(). ++ */ ++ ProgramDocWrapper(const std::string& bindingName, ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& ++ seeAlso) ++ { ++ util::ProgramDoc pd(programName, shortDocumentation, documentation, ++ seeAlso); ++ BindingInfo::RegisterProgramDoc(bindingName, pd); ++ } ++}; ++ ++} // namespace markdown ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/markdown/res/change_language.js b/src/mlpack/bindings/markdown/res/change_language.js +new file mode 100644 +index 000000000..4b520205f +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/change_language.js +@@ -0,0 +1,102 @@ ++/** ++ * A utility function to change the language displayed on the page. This ++ * function should be called whenever the language is changed from the ++ * drop-down. ++ */ ++function changeLanguage() ++{ ++ lang = document.getElementById("language-select").value; ++ var links = document.getElementsByClassName("language-link"); ++ for (i = 0; i < links.length; ++i) ++ { ++ // With each of the links, we get the inner
    , but we need the parent ++ //
  • . ++ if (links[i].id == lang || links[i].id == "always") ++ links[i].parentElement.style.display = "list-item"; ++ else ++ links[i].parentElement.style.display = "none"; ++ } ++ ++ var titles = document.getElementsByClassName("language-title"); ++ for (i = 0; i < titles.length; ++i) ++ { ++ if (titles[i].id == lang) ++ titles[i].style.display = "inline"; ++ else ++ titles[i].style.display = "none"; ++ } ++ ++ var headers = document.getElementsByClassName("language-header"); ++ for (i = 0; i < headers.length; ++i) ++ { ++ if (headers[i].id == lang) ++ headers[i].style.display = "inline"; ++ else ++ headers[i].style.display = "none"; ++ } ++ ++ var decls = document.getElementsByClassName("language-decl"); ++ for (i = 0; i < decls.length; ++i) ++ { ++ if (decls[i].id == lang) ++ decls[i].style.display = "inline"; ++ else ++ decls[i].style.display = "none"; ++ } ++ ++ var types = document.getElementsByClassName("language-types"); ++ for (i = 0; i < types.length; ++i) ++ { ++ if (types[i].id == lang) ++ types[i].style.display = "inline"; ++ else ++ types[i].style.display = "none"; ++ } ++ ++ var details = document.getElementsByClassName("language-detail-link"); ++ for (i = 0; i < details.length; ++i) ++ { ++ if (details[i].id == lang) ++ details[i].style.display = "inline"; ++ else ++ details[i].style.display = "none"; ++ } ++ ++ var sections = document.getElementsByClassName("language-section"); ++ for (i = 0; i < sections.length; ++i) ++ { ++ if (sections[i].id == lang) ++ sections[i].style.display = "inline"; ++ else ++ sections[i].style.display = "none"; ++ } ++} ++ ++document.body.onload = function() ++{ ++ // Do we need to manually set the language because the user came with an ++ // anchor? ++ if (window.location.hash) ++ { ++ // Try to extract the language. ++ firstUnderscore = window.location.hash.indexOf("_"); ++ if (firstUnderscore !== -1) ++ { ++ var lang = window.location.hash.substring(1, firstUnderscore); ++ // Now see if it's in the list of languages. ++ var select = document.getElementById("language-select"); ++ for (i = 0; i < select.length; ++i) ++ { ++ var select_lang = select[i].value; ++ // Is the language a match? ++ if (lang === select_lang) ++ { ++ select.value = select_lang; ++ break; ++ } ++ } ++ } ++ } ++ ++ changeLanguage(); ++} +diff --git a/src/mlpack/bindings/markdown/res/formatting.css b/src/mlpack/bindings/markdown/res/formatting.css +new file mode 100644 +index 000000000..487b026e7 +--- /dev/null ++++ b/src/mlpack/bindings/markdown/res/formatting.css +@@ -0,0 +1,142 @@ ++body ++{ ++ background: #000000; ++} ++ ++/* Don't display other languages by default. */ ++div.language-title, div.language-header, div.language-decl, ++div.language-detail-link, div.language-types, div.language-section ++{ ++ display: none; ++} ++ ++/* Just display cli documentation. */ ++div.language-title#cli, div.language-header#cli, div.language-decl#cli, ++div.language-detail-link#cli, div.language-types#cli, div.language-section#cli ++{ ++ display: none; ++} ++ ++div#header ++{ ++ padding-top: 10px; ++ width: 200px; ++ background: #000000; ++ border-right: 2px solid #333333; ++ height: 100%; ++ position: fixed; ++ z-index: 1; ++ top: 0; ++ left: 0; ++ overflow-y: scroll; ++} ++ ++div#header .language-select-div select ++{ ++ color: #ffffff; ++ background: transparent; ++ border: none; ++ height: 29px; ++ padding: 5px; ++ width: 190px; ++} ++ ++div#header .language-select-div ++{ ++ color: #ffffff; ++ border: 2px solid #333333; ++ background: url('../res/menu_bg.png') no-repeat 96% 0; ++ height: 34px; ++ width: 180px; ++ overflow: hidden; ++ margin-bottom: 20px; ++ ++ -webkit-border-radius: 5px; ++ -moz-border-radius: 5px; ++ border-radius: 5px; ++} ++ ++div#header ul ++{ ++ padding-top: 5px; ++ margin-bottom: 5px; ++ color: #bb2000; ++ display: table; ++ text-align: left; ++ padding-left: 0px; ++ margin-left: 15px; ++ font-size: 75%; ++} ++ ++div#header ul li ++{ ++ line-height: 1.3em; ++} ++ ++div#header ul li a ++{ ++ color: #eab72c; ++ font-weight: none; ++} ++ ++div#header ul li a:hover ++{ ++ color: #ffffff; ++ font-weight: none; ++} ++ ++div#docs ++{ ++ margin-left: 200px; ++} ++ ++span.special ++{ ++ font-size: 85%; ++ color: #eab72c; ++} ++ ++div.category ++{ ++ text-align: left; ++} ++ ++div.category h5 ++{ ++ text-align: left; ++ margin-top: 0; ++ margin-bottom: 0; ++ color: #ffffff; ++ font-size: 100%; ++ font-weight: normal; ++ padding: 0; ++} ++ ++h2#mlpack-overview, h2.language-types-h2 ++{ ++ padding-bottom: 0.75em; ++} ++ ++div.language-header h1 ++{ ++ color: #ffffff; ++ text-align: center; ++ border-top: none; ++ border-bottom: 1px solid #eab72c; ++} ++ ++div.language-types li code ++{ ++ font-weight: bold; ++ color: #eab72c; ++} ++ ++div.language-types li ++{ ++ padding-bottom: 1em; ++} ++ ++div.language-section table td a code:hover ++{ ++ color: #bb2000; ++} +diff --git a/src/mlpack/bindings/markdown/res/menu_bg.png b/src/mlpack/bindings/markdown/res/menu_bg.png +new file mode 100644 +index 0000000000000000000000000000000000000000..c29580a400267755c0f2a7d1b8f154d329797553 +GIT binary patch +literal 395 +zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I +zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@$z$e7@|Ns9$rm%z} +zkO5&YUc4A6vSP&wAh~+=Y9P6B<3=C}Q4qtiwFt=JD+%%oW?-oBD6-sRv)}%J<6##S +z{uAOr8O9`UcNcz%T?{vY9QG1VUsv|Wj9gsIB1StofUaP$^K@|xk+_^3kmk^E@KBlv +z3nQb?wiDJ01q~ZqKi}J1EyfTU{PA6^wa?xsyTXMZR(+r2Hbc2uaP~}(9I0R241Yd| +zZn0k0^$2K^YKdz^NlIc#s#S7PDv)9@GBC8%H89jQGzc*?wK6caGBVIL05S}G9 ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. This is for regular types. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of a matrix option, a tuple option, a ++ * serializable option, or a string option (this returns the default filename, ++ * or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */ = 0); ++ ++/** ++ * Return the default value of a model option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Return the default value of an option. This is the function that will be ++ * placed into the CLI functionMap. ++ */ ++template ++void DefaultParam(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ std::string* outstr = (std::string*) output; ++ *outstr = DefaultParamImpl::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++// Include implementation. ++#include "default_param_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/default_param_impl.hpp b/src/mlpack/bindings/python/default_param_impl.hpp +new file mode 100644 +index 000000000..bae57cce2 +--- /dev/null ++++ b/src/mlpack/bindings/python/default_param_impl.hpp +@@ -0,0 +1,147 @@ ++/** ++ * @file default_param_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Return the default value of a parameter, depending on its type. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_DEFAULT_PARAM_IMPL_HPP ++ ++#include "default_param.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return the default value of an option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type* /* junk */) ++{ ++ std::ostringstream oss; ++ if (std::is_same::value) ++ oss << "False"; ++ else ++ oss << boost::any_cast(data.value); ++ ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a vector option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ // Print each element in an array delimited by square brackets. ++ std::ostringstream oss; ++ const T& vector = boost::any_cast(data.value); ++ oss << "["; ++ if (std::is_same>::value) ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << "'" << vector[i] << "', "; ++ } ++ ++ oss << "'" << vector[vector.size() - 1] << "'"; ++ } ++ ++ oss << "]"; ++ } ++ else ++ { ++ if (vector.size() > 0) ++ { ++ for (size_t i = 0; i < vector.size() - 1; ++i) ++ { ++ oss << vector[i] << ", "; ++ } ++ ++ oss << vector[vector.size() - 1]; ++ } ++ ++ oss << "]"; ++ } ++ return oss.str(); ++} ++ ++/** ++ * Return the default value of a string option. ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& data, ++ const typename boost::enable_if>::type*) ++{ ++ const std::string& s = *boost::any_cast(&data.value); ++ return "'" + s + "'"; ++} ++ ++/** ++ * Return the default value of a matrix option (this returns the default ++ * filename, or '' if the default is no file). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::enable_if_c< ++ arma::is_arma_type::value || ++ std::is_same>::value>::type* /* junk */) ++{ ++ // Get the filename and return it, or return an empty string. ++ if (std::is_same::value || ++ std::is_same::value) ++ { ++ return "np.empty([0])"; ++ } ++ else if (std::is_same>::value || ++ std::is_same>::value) ++ { ++ return "np.empty([0], dtype=np.uint64)"; ++ } ++ else if (std::is_same>::value) ++ { ++ return "np.empty([0, 0], dtype=np.uint64)"; ++ } ++ else ++ { ++ return "np.empty([0, 0])"; ++ } ++} ++ ++/** ++ * Return the default value of a model option (always "None"). ++ */ ++template ++std::string DefaultParamImpl( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type* /* junk */, ++ const typename boost::enable_if>::type* /* junk */) ++{ ++ return "None"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_cython_type.hpp b/src/mlpack/bindings/python/get_cython_type.hpp +index e44f4f70b..e44b91eb0 100644 +--- a/src/mlpack/bindings/python/get_cython_type.hpp ++++ b/src/mlpack/bindings/python/get_cython_type.hpp +@@ -40,16 +40,6 @@ inline std::string GetCythonType( + return "int"; + } + +-template<> +-inline std::string GetCythonType( +- const util::ParamData& /* d */, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*, +- const typename boost::disable_if>::type*) +-{ +- return "float"; +-} +- + template<> + inline std::string GetCythonType( + const util::ParamData& /* d */, +diff --git a/src/mlpack/bindings/python/get_printable_type.hpp b/src/mlpack/bindings/python/get_printable_type.hpp +new file mode 100644 +index 000000000..51a528b4d +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type.hpp +@@ -0,0 +1,120 @@ ++/** ++ * @file get_printable_type.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_HPP ++ ++#include ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type* = 0); ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++template ++void GetPrintableType(const util::ParamData& d, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ GetPrintableType::type>(d); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "get_printable_type_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/get_printable_type_impl.hpp b/src/mlpack/bindings/python/get_printable_type_impl.hpp +new file mode 100644 +index 000000000..2031b3435 +--- /dev/null ++++ b/src/mlpack/bindings/python/get_printable_type_impl.hpp +@@ -0,0 +1,151 @@ ++/** ++ * @file get_printable_type_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Template metaprogramming to return the string representation of the Python ++ * type for a given Python binding parameter. ++ * ++ * mlpack 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 mlpack. If not, see ++ * http://www.opensource.org/licenses/BSD-3-Clause for more information. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_GET_PRINTABLE_TYPE_IMPL_HPP ++ ++#include "get_printable_type.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "unknown"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "int"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "double"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "string"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "size_t"; ++} ++ ++template<> ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "bool"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return "list of " + GetPrintableType(d) + "s"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ std::string type = "matrix"; ++ if (std::is_same::value) ++ { ++ if (T::is_row || T::is_col) ++ type = "vector"; ++ } ++ else if (std::is_same::value) ++ { ++ type = "int matrix"; ++ if (T::is_row || T::is_col) ++ type = "int vector"; ++ } ++ ++ return type; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& /* d */, ++ const typename boost::enable_if>>::type*) ++{ ++ return "categorical matrix"; ++} ++ ++template ++inline std::string GetPrintableType( ++ const util::ParamData& d, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ return d.cppType + "Type"; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_doc.hpp b/src/mlpack/bindings/python/print_doc.hpp +index 7a5a27781..b8962ccaf 100644 +--- a/src/mlpack/bindings/python/print_doc.hpp ++++ b/src/mlpack/bindings/python/print_doc.hpp +@@ -14,7 +14,7 @@ + + #include + #include +-#include "get_python_type.hpp" ++#include "get_printable_type.hpp" + + namespace mlpack { + namespace bindings { +@@ -44,24 +44,20 @@ void PrintDoc(const util::ParamData& d, + oss << d.name << "_ ("; + else + oss << d.name << " ("; +- oss << GetPythonType::type>(d) << "): " ++ oss << GetPrintableType::type>(d) << "): " + << d.desc; + + // Print a default, if possible. + if (!d.required) + { +- if (d.cppType == "std::string") ++ // Call the correct overload to get the default value directly. ++ if (d.cppType == "std::string" || d.cppType == "double" || ++ d.cppType == "int" || d.cppType == "std::vector" || ++ d.cppType == "std::vector" || ++ d.cppType == "std::vector") + { +- oss << " Default value '" << boost::any_cast(d.value) +- << "'."; +- } +- else if (d.cppType == "double") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; +- } +- else if (d.cppType == "int") +- { +- oss << " Default value " << boost::any_cast(d.value) << "."; ++ std::string defaultValue = DefaultParamImpl(d); ++ oss << " Default value " << defaultValue << "."; + } + } + +diff --git a/src/mlpack/bindings/python/print_doc_functions.hpp b/src/mlpack/bindings/python/print_doc_functions.hpp +index a9207c570..51e85b78d 100644 +--- a/src/mlpack/bindings/python/print_doc_functions.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions.hpp +@@ -19,6 +19,21 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName); ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName); ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo(); ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -29,6 +44,11 @@ inline std::string PrintValue(const T& value, bool quotes); + template<> + inline std::string PrintValue(const bool& value, bool quotes); + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName); ++ + // Recursion base case. + inline std::string PrintInputOptions(); + +@@ -57,6 +77,12 @@ std::string PrintOutputOptions(const std::string& paramName, + template + std::string ProgramCall(const std::string& programName, Args... args); + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. ++ */ ++inline std::string ProgramCall(const std::string& programName); ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +diff --git a/src/mlpack/bindings/python/print_doc_functions_impl.hpp b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +index c27e88609..0de6a5312 100644 +--- a/src/mlpack/bindings/python/print_doc_functions_impl.hpp ++++ b/src/mlpack/bindings/python/print_doc_functions_impl.hpp +@@ -19,6 +19,32 @@ namespace mlpack { + namespace bindings { + namespace python { + ++/** ++ * Given the name of a binding, print its Python name. ++ */ ++inline std::string GetBindingName(const std::string& bindingName) ++{ ++ // No modification is needed to the name---we just use it as-is. ++ return bindingName + "()"; ++} ++ ++/** ++ * Print any import information for the Python binding. ++ */ ++inline std::string PrintImport(const std::string& bindingName) ++{ ++ return "from mlpack import " + bindingName; ++} ++ ++/** ++ * Print any special information about output options. ++ */ ++inline std::string PrintOutputOptionInfo() ++{ ++ return "Results are returned in a Python dictionary. The keys of the " ++ "dictionary are the names of the output parameters."; ++} ++ + /** + * Given a parameter type, print the corresponding value. + */ +@@ -48,6 +74,23 @@ inline std::string PrintValue(const bool& value, bool quotes) + return "False"; + } + ++/** ++ * Given a parameter name, print its corresponding default value. ++ */ ++inline std::string PrintDefault(const std::string& paramName) ++{ ++ if (CLI::Parameters().count(paramName) == 0) ++ throw std::invalid_argument("unknown parameter " + paramName + "!"); ++ ++ const util::ParamData& d = CLI::Parameters()[paramName]; ++ ++ std::string defaultValue; ++ CLI::GetSingleton().functionMap[d.tname]["DefaultParam"](d, NULL, ++ (void*) &defaultValue); ++ ++ return defaultValue; ++} ++ + // Recursion base case. + std::string PrintInputOptions() { return ""; } + +@@ -136,7 +179,8 @@ std::string PrintOutputOptions(const std::string& paramName, + + /** + * Given a name of a binding and a variable number of arguments (and their +- * contents), print the corresponding function call. ++ * contents), print the corresponding function call. The given programName ++ * should not be the output of GetBindingName(). + */ + template + std::string ProgramCall(const std::string& programName, Args... args) +@@ -166,6 +210,76 @@ std::string ProgramCall(const std::string& programName, Args... args) + return util::HyphenateString(call, 2) + "\n" + oss.str(); + } + ++/** ++ * Given the name of a binding, print a program call assuming that all options ++ * are specified. The programName should not be the output of GetBindingName(). ++ */ ++inline std::string ProgramCall(const std::string& programName) ++{ ++ std::ostringstream oss; ++ oss << ">>> "; ++ ++ // Determine if we have any output options. ++ const std::map& parameters = CLI::Parameters(); ++ bool hasOutput = false; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input) ++ { ++ hasOutput = true; ++ break; ++ } ++ } ++ ++ if (hasOutput) ++ oss << "d = "; ++ ++ oss << programName << "("; ++ ++ // Now iterate over every input option. ++ bool first = true; ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (!it->second.input || it->second.persistent) ++ continue; ++ ++ if (!first) ++ oss << ", "; ++ else ++ first = false; ++ ++ // Print the input option. ++ if (it->second.name != "lambda") // Don't print Python keywords. ++ oss << it->second.name << "="; ++ else ++ oss << it->second.name << "_="; ++ ++ std::string value; ++ CLI::GetSingleton().functionMap[it->second.tname]["DefaultParam"]( ++ it->second, NULL, (void*) &value); ++ oss << value; ++ } ++ oss << ")"; ++ ++ std::string result = util::HyphenateString(oss.str(), 8); ++ ++ oss.str(""); ++ oss << result; ++ ++ // Now print output lines. ++ for (auto it = parameters.begin(); it != parameters.end(); ++it) ++ { ++ if (it->second.input) ++ continue; ++ ++ // Print a new line for the output option. ++ oss << std::endl << ">>> " << it->second.name << " = d['" ++ << it->second.name << "']"; ++ } ++ ++ return oss.str(); ++} ++ + /** + * Given the name of a model, print it. Here we do not need to modify anything. + */ +@@ -183,14 +297,6 @@ inline std::string PrintDataset(const std::string& datasetName) + return "'" + datasetName + "'"; + } + +-/** +- * Given the name of a binding, print its invocation. +- */ +-inline std::string ProgramCall(const std::string& programName) +-{ +- return ">>> " + programName + "("; +-} +- + /** + * Print any closing call to a program. For a Python binding this is a closing + * brace. +diff --git a/src/mlpack/bindings/python/print_input_processing.hpp b/src/mlpack/bindings/python/print_input_processing.hpp +index e271906cb..04f318a7c 100644 +--- a/src/mlpack/bindings/python/print_input_processing.hpp ++++ b/src/mlpack/bindings/python/print_input_processing.hpp +@@ -17,7 +17,6 @@ + #include "get_numpy_type.hpp" + #include "get_numpy_type_char.hpp" + #include "get_cython_type.hpp" +-#include "get_python_type.hpp" + #include "strip_type.hpp" + + namespace mlpack { +diff --git a/src/mlpack/bindings/python/print_type_doc.hpp b/src/mlpack/bindings/python/print_type_doc.hpp +new file mode 100644 +index 000000000..9a5d6dc71 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc.hpp +@@ -0,0 +1,81 @@ ++/** ++ * @file print_type_doc.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type, detailing what the type actually is to ++ * the user. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_HPP ++ ++#include ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::disable_if>>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if>::value>::type* = 0); ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type* = 0, ++ const typename boost::enable_if>::type* = 0); ++ ++/** ++ * Print the command-line type of an option into a string. ++ */ ++template ++void PrintTypeDoc(const util::ParamData& data, ++ const void* /* input */, ++ void* output) ++{ ++ *((std::string*) output) = ++ PrintTypeDoc::type>(data); ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#include "print_type_doc_impl.hpp" ++ ++#endif +diff --git a/src/mlpack/bindings/python/print_type_doc_impl.hpp b/src/mlpack/bindings/python/print_type_doc_impl.hpp +new file mode 100644 +index 000000000..8d6ae3971 +--- /dev/null ++++ b/src/mlpack/bindings/python/print_type_doc_impl.hpp +@@ -0,0 +1,162 @@ ++/** ++ * @file print_type_doc_impl.hpp ++ * @author Ryan Curtin ++ * ++ * Print documentation for a given type. ++ */ ++#ifndef MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++#define MLPACK_BINDINGS_PYTHON_PRINT_TYPE_DOC_IMPL_HPP ++ ++#include "print_type_doc.hpp" ++ ++namespace mlpack { ++namespace bindings { ++namespace python { ++ ++/** ++ * Return a string representing the command-line type of an option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>::type*, ++ const typename boost::disable_if>>::type*) ++{ ++ // A flag type. ++ if (std::is_same::value) ++ { ++ return "A boolean flag option (True or False)."; ++ } ++ // An integer. ++ else if (std::is_same::value) ++ { ++ return "An integer (i.e., \"1\")."; ++ } ++ // A floating point value. ++ else if (std::is_same::value) ++ { ++ return "A floating-point number (i.e., \"0.5\")."; ++ } ++ // A string. ++ else if (std::is_same::value) ++ { ++ return "A character string (i.e., \"hello\")."; ++ } ++ // Not sure what it is... ++ else ++ { ++ throw std::invalid_argument("unknown parameter type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a vector. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same>::value) ++ { ++ return "A list of integers; i.e., [0, 1, 2]."; ++ } ++ else if (std::is_same>::value) ++ { ++ return "A list of strings; i.e., [\"hello\", \"goodbye\"]."; ++ } ++ else ++ { ++ throw std::invalid_argument("unknown vector type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& data, ++ const typename std::enable_if::value>::type*) ++{ ++ if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data. This can be a 2-d matrix where " ++ "one dimension has size 1, or it can also be a list, a numpy 1-d " ++ "ndarray, or a 1-d pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data. This can be a list of lists, a " ++ "numpy ndarray, or a pandas DataFrame. If the dtype is not already " ++ "float64, it will be converted."; ++ } ++ } ++ else if (std::is_same::value) ++ { ++ if (T::is_col || T::is_row) ++ { ++ return "A 1-d arraylike containing data with a uint64 dtype. This can be" ++ " a 2-d matrix where one dimension has size 1, or it can also be a " ++ "list, a numpy 1-d ndarray, or a 1-d pandas DataFrame. If the dtype " ++ "is not already uint64, it will be converted."; ++ } ++ else ++ { ++ return "A 2-d arraylike containing data with a uint64 dtype. This can " ++ "be a list of lists, a numpy ndarray, or a pandas DataFrame. If the " ++ "dtype is not already uint64, it will be converted."; ++ } ++ } ++ else ++ { ++ throw std::invalid_argument("unknown matrix type " + data.cppType); ++ } ++} ++ ++/** ++ * Return a string representing the command-line type of a matrix tuple option. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename std::enable_if>::value>::type*) ++{ ++ return "A 2-d arraylike containing data. Like the regular 2-d matrices, this" ++ " can be a list of lists, a numpy ndarray, or a pandas DataFrame. " ++ "However, this type can also accept a pandas DataFrame that has columns " ++ "of type 'CategoricalDtype'. These categorical values will be converted " ++ "to numeric indices before being passed to mlpack, and then inside mlpack" ++ " they will be properly treated as categorical variables, so there is no " ++ "need to do one-hot encoding for this matrix type. If the dtype of the " ++ "given matrix is not already float64, it will be converted."; ++} ++ ++/** ++ * Return a string representing the command-line type of a model. ++ */ ++template ++std::string PrintTypeDoc( ++ const util::ParamData& /* data */, ++ const typename boost::disable_if>::type*, ++ const typename boost::enable_if>::type*) ++{ ++ return "An mlpack model pointer. This type can be pickled to or from disk, " ++ "and internally holds a pointer to C++ memory containing the mlpack " ++ "model. Note that this means that the mlpack model itself cannot be " ++ "easily inspected in Python; however, the pickled model can be loaded " ++ "in C++ and inspected there."; ++} ++ ++} // namespace python ++} // namespace bindings ++} // namespace mlpack ++ ++#endif +diff --git a/src/mlpack/bindings/python/py_option.hpp b/src/mlpack/bindings/python/py_option.hpp +index 98bba2dd5..e175d282b 100644 +--- a/src/mlpack/bindings/python/py_option.hpp ++++ b/src/mlpack/bindings/python/py_option.hpp +@@ -13,6 +13,7 @@ + #define MLPACK_BINDINGS_PYTHON_PY_OPTION_HPP + + #include ++#include "default_param.hpp" + #include "get_param.hpp" + #include "get_printable_param.hpp" + #include "print_class_defn.hpp" +@@ -85,6 +86,9 @@ class PyOption + CLI::GetSingleton().functionMap[data.tname]["GetPrintableParam"] = + &GetPrintableParam; + ++ CLI::GetSingleton().functionMap[data.tname]["DefaultParam"] = ++ &DefaultParam; ++ + // These are used by the pyx generator. + CLI::GetSingleton().functionMap[data.tname]["PrintClassDefn"] = + &PrintClassDefn; +diff --git a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +index cf3c1055d..93daaddc4 100644 +--- a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp ++++ b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +@@ -19,6 +19,7 @@ using namespace mlpack; + using namespace mlpack::kernel; + + PROGRAM_INFO("Python binding test", ++ "A simple program to test Python binding functionality.", + "A simple program to test Python binding functionality. You can build " + "mlpack with the BUILD_TESTS option set to off, and this binding will " + "no longer be built."); +diff --git a/src/mlpack/core/util/cli.cpp b/src/mlpack/core/util/cli.cpp +index f2bf6cec2..1a83080bd 100644 +--- a/src/mlpack/core/util/cli.cpp ++++ b/src/mlpack/core/util/cli.cpp +@@ -27,7 +27,8 @@ using namespace mlpack; + using namespace mlpack::util; + + // Fake ProgramDoc in case none is supplied. +-static ProgramDoc emptyProgramDoc = ProgramDoc("", []() { return ""; }); ++static ProgramDoc emptyProgramDoc = ProgramDoc("", "", []() { return ""; }, ++ {}); + + /* Constructors, Destructors, Copy */ + /* Make the constructor private, to preclude unauthorized instances */ +diff --git a/src/mlpack/core/util/mlpack_main.hpp b/src/mlpack/core/util/mlpack_main.hpp +index 4c915c0c0..8003f38ac 100644 +--- a/src/mlpack/core/util/mlpack_main.hpp ++++ b/src/mlpack/core/util/mlpack_main.hpp +@@ -21,6 +21,7 @@ + #define BINDING_TYPE_CLI 0 + #define BINDING_TYPE_TEST 1 + #define BINDING_TYPE_PYX 2 ++#define BINDING_TYPE_MARKDOWN 128 + #define BINDING_TYPE_UNKNOWN -1 + + #ifndef BINDING_TYPE +@@ -99,9 +100,10 @@ using Option = mlpack::bindings::tests::TestOption; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }) + + #elif(BINDING_TYPE == BINDING_TYPE_PYX) // This is a Python binding. + +@@ -128,9 +130,10 @@ static const std::string testName = ""; + #include + + #undef PROGRAM_INFO +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }); \ ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ }); \ + namespace mlpack { \ + namespace bindings { \ + namespace python { \ +@@ -148,6 +151,59 @@ PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" + + // Nothing else needs to be defined---the binding will use mlpackMain() as-is. + ++#elif BINDING_TYPE == BINDING_TYPE_MARKDOWN ++ ++// It doesn't really matter whether this is true or false... ++#define BINDING_MATRIX_TRANSPOSED true ++ ++// We use BINDING_NAME in PROGRAM_INFO() so it needs to be defined. ++#ifndef BINDING_NAME ++ #error "BINDING_NAME must be defined when BINDING_TYPE is Markdown!" ++#endif ++ ++#include ++#include ++ ++#define PRINT_PARAM_STRING mlpack::bindings::markdown::ParamString ++#define PRINT_PARAM_VALUE mlpack::bindings::markdown::PrintValue ++#define PRINT_DATASET mlpack::bindings::markdown::PrintDataset ++#define PRINT_MODEL mlpack::bindings::markdown::PrintModel ++#define PRINT_CALL mlpack::bindings::markdown::ProgramCall ++#define BINDING_IGNORE_CHECK mlpack::bindings::markdown::IgnoreCheck ++ ++namespace mlpack { ++namespace util { ++ ++template ++using Option = mlpack::bindings::markdown::MDOption; ++ ++} ++} ++ ++#include ++#include ++ ++#undef PROGRAM_INFO ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) static \ ++ mlpack::bindings::markdown::ProgramDocWrapper \ ++ cli_programdoc_dummy_object = \ ++ mlpack::bindings::markdown::ProgramDocWrapper(BINDING_NAME, NAME, \ ++ SHORT_DESC, []() { return DESC; }, { __VA_ARGS__ }); \ ++ ++PARAM_FLAG("verbose", "Display informational messages and the full list of " ++ "parameters and timers at the end of execution.", "v"); ++ ++// CLI-specific parameters. ++PARAM_FLAG("help", "Default help info.", "h"); ++PARAM_STRING_IN("info", "Print help on a specific option.", "", ""); ++PARAM_FLAG("version", "Display the version of mlpack.", "V"); ++ ++// Python-specific parameters. ++PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" ++ " copied before the method is run. This is useful for debugging problems " ++ "where the input parameters are being modified by the algorithm, but can " ++ "slow down the code.", ""); ++ + #else + + #error "Unknown binding type! Be sure BINDING_TYPE is defined if you are " \ +diff --git a/src/mlpack/core/util/param.hpp b/src/mlpack/core/util/param.hpp +index b812887fb..de50f5b00 100644 +--- a/src/mlpack/core/util/param.hpp ++++ b/src/mlpack/core/util/param.hpp +@@ -29,6 +29,21 @@ using DatasetInfo = DatasetMapper; + } // namespace data + } // namespace mlpack + ++/** ++ * Provide a link for a binding's "see also" documentation section, which is ++ * primarily (but not necessarily exclusively) used by the Markdown bindings ++ * This link can be specified by calling SEE_ALSO("description", "link"), where ++ * "description" is the description of the link and "link" may be one of the ++ * following: ++ * ++ * - A direct URL, starting with http:// or https://. ++ * - A page anchor for documentation, referencing another binding by its CMake ++ * binding name, i.e. "#knn". ++ * - A link to a Doxygen page, using the mangled Doxygen name after a ++ * '@doxygen/', i.e., "@doxygen/mlpack1_1_adaboost1_1_AdaBoost". ++ */ ++#define SEE_ALSO(DESCRIPTION, LINK) {DESCRIPTION, LINK} ++ + /** + * Document an executable. Only one instance of this macro should be + * present in your program! Therefore, use it in the main.cpp +@@ -41,14 +56,22 @@ using DatasetInfo = DatasetMapper; + * PARAM_DOUBLE_OUT_REQ(), PARAM_VECTOR_OUT_REQ(), PARAM_STRING_OUT_REQ(). + * + * @param NAME Short string representing the name of the program. ++ * @param SHORT_DESC Short two-sentence description of the program; it should ++ * describe what the program implements and does, and a quick overview of ++ * how it can be used and what it should be used for. + * @param DESC Long string describing what the program does and possibly a + * simple usage example. Newlines should not be used here; this is taken + * care of by CLI (however, you can explicitly specify newlines to denote +- * new paragraphs). ++ * new paragraphs). You can also use printing macros like ++ * PRINT_PARAM_STRING(), PRINT_DATASET(), and others. ++ * @param SEE_ALSOS A set of SEE_ALSO() macros that are used for generating ++ * documentation. See the SEE_ALSO() macro. This is a varargs argument, so ++ * you can add as many SEE_ALSO()s as you like. + */ +-#define PROGRAM_INFO(NAME, DESC) static mlpack::util::ProgramDoc \ +- cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, \ +- []() { return DESC; }) ++#define PROGRAM_INFO(NAME, SHORT_DESC, DESC, ...) \ ++ static mlpack::util::ProgramDoc \ ++ cli_programdoc_dummy_object = mlpack::util::ProgramDoc(NAME, SHORT_DESC, \ ++ []() { return DESC; }, { __VA_ARGS__ } ) + + /** + * Define a flag parameter. +diff --git a/src/mlpack/core/util/program_doc.cpp b/src/mlpack/core/util/program_doc.cpp +index 814573b1c..28d7ee8b8 100644 +--- a/src/mlpack/core/util/program_doc.cpp ++++ b/src/mlpack/core/util/program_doc.cpp +@@ -23,17 +23,33 @@ using namespace std; + * Construct a ProgramDoc object. When constructed, it will register itself + * with CLI. A fatal error will be thrown if more than one is constructed. + * +- * @param programName Short string representing the name of the program. +- * @param documentation Long string containing documentation on how to use the +- * program and what it is. No newline characters are necessary; this is +- * taken care of by CLI later. + * @param defaultModule Name of the default module. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. ++ * @param documentation Long string containing documentation on how to use the ++ * program and what it is. No newline characters are necessary; this is ++ * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ +-ProgramDoc::ProgramDoc(const std::string& programName, +- const std::function& documentation) : ++ProgramDoc::ProgramDoc( ++ const std::string& programName, ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso) : + programName(programName), +- documentation(documentation) ++ shortDocumentation(shortDocumentation), ++ documentation(documentation), ++ seeAlso(seeAlso) + { + // Register this with CLI. + CLI::RegisterProgramDoc(this); + } ++ ++/** ++ * Construct an empty ProgramDoc object. ++ */ ++ProgramDoc::ProgramDoc() ++{ ++ CLI::RegisterProgramDoc(this); ++} +diff --git a/src/mlpack/core/util/program_doc.hpp b/src/mlpack/core/util/program_doc.hpp +index f5de89fe5..296b15586 100644 +--- a/src/mlpack/core/util/program_doc.hpp ++++ b/src/mlpack/core/util/program_doc.hpp +@@ -33,17 +33,32 @@ class ProgramDoc + * will be returned. + * + * @param programName Short string representing the name of the program. ++ * @param shortDocumentation A short two-sentence description of the program, ++ * what it does, and what it is useful for. + * @param documentation Long string containing documentation on how to use the + * program and what it is. No newline characters are necessary; this is + * taken care of by CLI later. ++ * @param seeAlso A set of pairs of strings with useful "see also" ++ * information; each pair is . + */ + ProgramDoc(const std::string& programName, +- const std::function& documentation); ++ const std::string& shortDocumentation, ++ const std::function& documentation, ++ const std::vector>& seeAlso); ++ ++ /** ++ * Construct an empty ProgramDoc object. (This is not meant to be used!) ++ */ ++ ProgramDoc(); + + //! The name of the program. + std::string programName; ++ //! The short documentation for the program. ++ std::string shortDocumentation; + //! Documentation for what the program does. + std::function documentation; ++ //! Set of see also information. ++ std::vector> seeAlso; + }; + + } // namespace util +diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt +index 53fc57f75..f4b4185c8 100644 +--- a/src/mlpack/methods/CMakeLists.txt ++++ b/src/mlpack/methods/CMakeLists.txt +@@ -53,3 +53,7 @@ endforeach() + + set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + set(MLPACK_PYXS ${MLPACK_PYXS} PARENT_SCOPE) ++set(MARKDOWN_SRCS ${MARKDOWN_SRCS} PARENT_SCOPE) ++set(MARKDOWN_NAMES ${MARKDOWN_NAMES} PARENT_SCOPE) ++set(MARKDOWN_NAME_CATEGORIES ${MARKDOWN_NAME_CATEGORIES} PARENT_SCOPE) ++set(MARKDOWN_ALL_LANGUAGES_LIST ${MARKDOWN_ALL_LANGUAGES_LIST} PARENT_SCOPE) +diff --git a/src/mlpack/methods/adaboost/CMakeLists.txt b/src/mlpack/methods/adaboost/CMakeLists.txt +index 583131ad4..f940729a1 100644 +--- a/src/mlpack/methods/adaboost/CMakeLists.txt ++++ b/src/mlpack/methods/adaboost/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(adaboost) + add_python_binding(adaboost) ++add_markdown_docs(adaboost "cli;python" "classification") +diff --git a/src/mlpack/methods/adaboost/adaboost_main.cpp b/src/mlpack/methods/adaboost/adaboost_main.cpp +index 0c3be12e2..ced3d3c1f 100644 +--- a/src/mlpack/methods/adaboost/adaboost_main.cpp ++++ b/src/mlpack/methods/adaboost/adaboost_main.cpp +@@ -46,7 +46,14 @@ using namespace mlpack::decision_stump; + using namespace mlpack::perceptron; + using namespace mlpack::util; + +-PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " ++PROGRAM_INFO("AdaBoost", ++ // Short description. ++ "An implementation of the AdaBoost.MH (Adaptive Boosting) algorithm for " ++ "classification. This can be used to train an AdaBoost model on labeled " ++ "data or use an existing AdaBoost model to predict the classes of new " ++ "points.", ++ // Long description. ++ "This program implements the AdaBoost (or Adaptive " + "Boosting) algorithm. The variant of AdaBoost implemented here is " + "AdaBoost.MH. It uses a weak learner, either decision stumps or " + "perceptrons, and over many iterations, creates a strong learner that is a " +@@ -72,7 +79,7 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + "classes for each point in the test dataset are output to the " + + PRINT_PARAM_STRING("output") + " output parameter. The AdaBoost model " + "itself is output to the " + PRINT_PARAM_STRING("output_model") + +- "output parameter." ++ " output parameter." + "\n\n" + "For example, to run AdaBoost on an input dataset " + + PRINT_DATASET("data") + " with perceptrons as the weak learner type, " +@@ -88,7 +95,15 @@ PROGRAM_INFO("AdaBoost", "This program implements the AdaBoost (or Adaptive " + PRINT_DATASET("predictions") + " with the following command: " + "\n\n" + + PRINT_CALL("adaboost", "input_model", "model", "test", "test_data", +- "output", "predictions")); ++ "output", "predictions"), ++ // See also... ++ SEE_ALSO("AdaBoost on Wikipedia", "https://en.wikipedia.org/wiki/AdaBoost"), ++ SEE_ALSO("Improved boosting algorithms using confidence-rated predictions " ++ "(pdf)", "http://rob.schapire.net/papers/SchapireSi98.pdf"), ++ SEE_ALSO("Perceptron", "#perceptron"), ++ SEE_ALSO("Decision Stump", "#decision_stump"), ++ SEE_ALSO("mlpack::adaboost::AdaBoost C++ class documentation", ++ "@doxygen/classmlpack_1_1adaboost_1_1AdaBoost.html")); + + // Input for training. + PARAM_MATRIX_IN("training", "Dataset for training AdaBoost.", "t"); +diff --git a/src/mlpack/methods/approx_kfn/CMakeLists.txt b/src/mlpack/methods/approx_kfn/CMakeLists.txt +index da20af02b..be1a5c4f0 100644 +--- a/src/mlpack/methods/approx_kfn/CMakeLists.txt ++++ b/src/mlpack/methods/approx_kfn/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # This program computes approximate furthest neighbors. + add_cli_executable(approx_kfn) + add_python_binding(approx_kfn) ++add_markdown_docs(approx_kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +index 9adce050e..7091536fd 100644 +--- a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp ++++ b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +@@ -22,6 +22,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Approximate furthest neighbor search", ++ // Short description. ++ "An implementation of two strategies for furthest neighbor search. This " ++ "can be used to compute the furthest neighbor of query point(s) from a set " ++ "of points; furthest neighbor models can be saved and reused with future " ++ "query point(s).", ++ // Long description. + "This program implements two strategies for furthest neighbor search. " + "These strategies are:" + "\n\n" +@@ -86,7 +92,18 @@ PROGRAM_INFO("Approximate furthest neighbor search", + PRINT_DATASET("neighbors") + " by calling" + "\n\n" + + PRINT_CALL("approx_kfn", "input_model", "model", "query", "new_query_set", +- "k", 3, "neighbors", "neighbors")); ++ "k", 3, "neighbors", "neighbors"), ++ SEE_ALSO("k-furthest-neighbor search", "#kfn"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Fast approximate furthest neighbors with data-dependent candidate" ++ " selection (pdf)", "http://ratml.org/pub/pdf/2016fast.pdf"), ++ SEE_ALSO("Approximate furthest neighbor in high dimensions (pdf)", ++ "https://pdfs.semanticscholar.org/a4b5/7b9cbf37201fb1d9a56c0f4eefad0466" ++ "9c20.pdf"), ++ SEE_ALSO("mlpack::neighbor::QDAFN class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1QDAFN.html"), ++ SEE_ALSO("mlpack::neighbor::DrusillaSelect class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1DrusillaSelect.html")); + + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); + PARAM_MATRIX_IN("query", "Matrix containing query points.", "q"); +diff --git a/src/mlpack/methods/cf/CMakeLists.txt b/src/mlpack/methods/cf/CMakeLists.txt +index 01796f29d..976304458 100644 +--- a/src/mlpack/methods/cf/CMakeLists.txt ++++ b/src/mlpack/methods/cf/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(cf) + add_python_binding(cf) ++add_markdown_docs(cf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp +index 237fc7267..743e9007c 100644 +--- a/src/mlpack/methods/cf/cf_main.cpp ++++ b/src/mlpack/methods/cf/cf_main.cpp +@@ -30,7 +30,13 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " ++PROGRAM_INFO("Collaborative Filtering", ++ // Short description. ++ "An implementation of several collaborative filtering (CF) techniques for " ++ "recommender systems. This can be used to train a new CF model, or use an" ++ " existing CF model to compute recommendations.", ++ // Long description. ++ "This program performs collaborative " + "filtering (CF) on the given dataset. Given a list of user, item and " + "preferences (the " + PRINT_PARAM_STRING("training") + " parameter), " + "the program will perform a matrix decomposition and then can perform a " +@@ -53,7 +59,7 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "addition, the number of recommendations per user to generate can be " + "specified with the " + PRINT_PARAM_STRING("recommendations") + " " + "parameter, and the number of similar users (the size of the neighborhood) " +- " to be considered when generating recommendations can be specified with " ++ "to be considered when generating recommendations can be specified with " + "the " + PRINT_PARAM_STRING("neighborhood") + " parameter." + "\n\n" + "For performing the matrix decomposition, the following optimization " +@@ -83,7 +89,20 @@ PROGRAM_INFO("Collaborative Filtering", "This program performs collaborative " + "call " + "\n\n" + + PRINT_CALL("cf", "input_model", "model", "query", "users", +- "recommendations", 5, "output", "recommendations")); ++ "recommendations", 5, "output", "recommendations"), ++ SEE_ALSO("Collaborative filtering tutorial", "@doxygen/cftutorial.html"), ++ SEE_ALSO("Alternating Matrix Factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Collaborative Filtering on Wikipedia", ++ "https://en.wikipedia.org/wiki/Collaborative_filtering"), ++ SEE_ALSO("Matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Matrix_factorization_" ++ "(recommender_systems)"), ++ SEE_ALSO("Matrix factorization techniques for recommender systems (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.441.3234" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::cf::CFType class documentation", ++ "@doxygen/classmlpack_1_1cf_1_1CFType.html")); + + // Parameters for training a model. + PARAM_MATRIX_IN("training", "Input dataset to perform CF on.", "t"); +diff --git a/src/mlpack/methods/dbscan/CMakeLists.txt b/src/mlpack/methods/dbscan/CMakeLists.txt +index 70939c9d0..47b79d782 100644 +--- a/src/mlpack/methods/dbscan/CMakeLists.txt ++++ b/src/mlpack/methods/dbscan/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(dbscan) + add_python_binding(dbscan) ++add_markdown_docs(dbscan "cli;python" "clustering") +diff --git a/src/mlpack/methods/dbscan/dbscan_main.cpp b/src/mlpack/methods/dbscan/dbscan_main.cpp +index c4afc3c85..04bcfc85f 100644 +--- a/src/mlpack/methods/dbscan/dbscan_main.cpp ++++ b/src/mlpack/methods/dbscan/dbscan_main.cpp +@@ -27,6 +27,10 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("DBSCAN clustering", ++ // Short description. ++ "An implementation of DBSCAN clustering. Given a dataset, this can " ++ "compute and return a clustering of that dataset.", ++ // Long description. + "This program implements the DBSCAN algorithm for clustering using " + "accelerated tree-based range search. The type of tree that is used " + "may be parameterized, or brute-force range search may also be used." +@@ -59,7 +63,13 @@ PROGRAM_INFO("DBSCAN clustering", + PRINT_DATASET("input") + " with a radius of 0.5 and a minimum cluster size" + " of 5 is given below:" + "\n\n" + +- PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5)); ++ PRINT_CALL("dbscan", "input", "input", "epsilon", 0.5, "min_size", 5), ++ SEE_ALSO("DBSCAN on Wikipedia", "https://en.wikipedia.org/wiki/DBSCAN"), ++ SEE_ALSO("A density-based algorithm for discovering clusters in large " ++ "spatial databases with noise (pdf)", ++ "http://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf"), ++ SEE_ALSO("mlpack::dbscan::DBSCAN class documentation", ++ "@doxygen/classmlpack_1_1dbscan_1_1DBSCAN.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to cluster.", "i"); + PARAM_UROW_OUT("assignments", "Output matrix for assignments of each " +diff --git a/src/mlpack/methods/decision_stump/CMakeLists.txt b/src/mlpack/methods/decision_stump/CMakeLists.txt +index 26042b443..3684a69fb 100644 +--- a/src/mlpack/methods/decision_stump/CMakeLists.txt ++++ b/src/mlpack/methods/decision_stump/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_stump) + add_python_binding(decision_stump) ++add_markdown_docs(decision_stump "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_stump/decision_stump_main.cpp b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +index aa4d9a67d..6f8f308af 100644 +--- a/src/mlpack/methods/decision_stump/decision_stump_main.cpp ++++ b/src/mlpack/methods/decision_stump/decision_stump_main.cpp +@@ -22,6 +22,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Decision Stump", ++ // Short description. ++ "An implementation of a decision stump, which is a single-level decision " ++ "tree. Given labeled data, a new decision stump can be trained; or, an " ++ "existing decision stump can be used to classify points.", ++ // Long description. + "This program implements a decision stump, which is a single-level decision" + " tree. The decision stump will split on one dimension of the input data, " + "and will split into multiple buckets. The dimension and bins are selected" +@@ -61,7 +66,12 @@ PROGRAM_INFO("Decision Stump", + "\n\n" + "After training, a decision stump can be saved with the " + + PRINT_PARAM_STRING("output_model") + " output parameter. That stump may " +- "later be re-used in subsequent calls to this program (or others)."); ++ "later be re-used in subsequent calls to this program (or others).", ++ SEE_ALSO("Decision tree", "#decision_tree"), ++ SEE_ALSO("Decision stumps on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_stump"), ++ SEE_ALSO("mlpack::decision_stump::DecisionStump class documentation", ++ "@doxygen/classmlpack_1_1decision__stump_1_1DecisionStump.html")); + + // Datasets we might load. + PARAM_MATRIX_IN("training", "The dataset to train on.", "t"); +diff --git a/src/mlpack/methods/decision_tree/CMakeLists.txt b/src/mlpack/methods/decision_tree/CMakeLists.txt +index 25598133b..bdc2ec6d5 100644 +--- a/src/mlpack/methods/decision_tree/CMakeLists.txt ++++ b/src/mlpack/methods/decision_tree/CMakeLists.txt +@@ -25,3 +25,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(decision_tree) + add_python_binding(decision_tree) ++add_markdown_docs(decision_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/decision_tree/decision_tree_main.cpp b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +index 8369312d1..7db31b725 100644 +--- a/src/mlpack/methods/decision_tree/decision_tree_main.cpp ++++ b/src/mlpack/methods/decision_tree/decision_tree_main.cpp +@@ -21,6 +21,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Decision tree", ++ // Short description. ++ "An implementation of an ID3-style decision tree for classification, which" ++ " supports categorical data. Given labeled data with numeric or " ++ "categorical features, a decision tree can be trained and saved; or, an " ++ "existing decision tree can be used for classification on new points.", ++ // Long description. + "Train and evaluate using a decision tree. Given a dataset containing " + "numeric or categorical features, and associated labels for each point in " + "the dataset, this program can train a decision tree on that data." +@@ -70,7 +76,15 @@ PROGRAM_INFO("Decision tree", + PRINT_DATASET("predictions") + ", one could call " + "\n\n" + + PRINT_CALL("decision_tree", "input_model", "tree", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("Decision stump", "#decision_stump"), ++ SEE_ALSO("Random forest", "#random_forest"), ++ SEE_ALSO("Decision trees on Wikipedia", ++ "https://en.wikipedia.org/wiki/Decision_tree_learning"), ++ SEE_ALSO("Induction of Decision Trees (pdf)", ++ "https://link.springer.com/content/pdf/10.1007/BF00116251.pdf"), ++ SEE_ALSO("mlpack::tree::DecisionTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1DecisionTree.html")); + + // Datasets. + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", +diff --git a/src/mlpack/methods/det/CMakeLists.txt b/src/mlpack/methods/det/CMakeLists.txt +index 762c7b11c..58b402c7c 100644 +--- a/src/mlpack/methods/det/CMakeLists.txt ++++ b/src/mlpack/methods/det/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(det) + add_python_binding(det) ++add_markdown_docs(det "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp +index 135baa703..df2db8169 100644 +--- a/src/mlpack/methods/det/det_main.cpp ++++ b/src/mlpack/methods/det/det_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Density Estimation With Density Estimation Trees", ++ // Short description. ++ "An implementation of density estimation trees for the density estimation " ++ "task. Density estimation trees can be trained or used to predict the " ++ "density at locations given by query points.", ++ // Long description. + "This program performs a number of functions related to Density Estimation " + "Trees. The optimal Density Estimation Tree (DET) can be trained on a set " + "of data (specified by " + PRINT_PARAM_STRING("training") + ") using " +@@ -49,7 +54,15 @@ PROGRAM_INFO("Density Estimation With Density Estimation Trees", + "trained on the given training points, or a tree given as the parameter " + + PRINT_PARAM_STRING("input_model") + ". The density estimates for the test" + " points may be saved using the " + +- PRINT_PARAM_STRING("test_set_estimates") + " output parameter."); ++ PRINT_PARAM_STRING("test_set_estimates") + " output parameter.", ++ SEE_ALSO("Density estimation tree (DET) tutorial", ++ "@doxygen/dettutorial.html"), ++ SEE_ALSO("Density estimation on Wikipedia", ++ "https://en.wikipedia.org/wiki/Density_estimation"), ++ SEE_ALSO("Density estimation trees (pdf)", ++ "http://www.mlpack.org/papers/det.pdf"), ++ SEE_ALSO("mlpack::tree::DTree class documentation", ++ "@doxygen/classmlpack_1_1det_1_1DTree.html")); + + // Input data files. + PARAM_MATRIX_IN("training", "The data set on which to build a density " +diff --git a/src/mlpack/methods/emst/CMakeLists.txt b/src/mlpack/methods/emst/CMakeLists.txt +index 73cd431cb..7a073a8f9 100644 +--- a/src/mlpack/methods/emst/CMakeLists.txt ++++ b/src/mlpack/methods/emst/CMakeLists.txt +@@ -23,3 +23,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(emst) + add_python_binding(emst) ++add_markdown_docs(emst "cli;python" "geometry") +diff --git a/src/mlpack/methods/emst/emst_main.cpp b/src/mlpack/methods/emst/emst_main.cpp +index 3b0f9de98..e60ba7b64 100644 +--- a/src/mlpack/methods/emst/emst_main.cpp ++++ b/src/mlpack/methods/emst/emst_main.cpp +@@ -31,6 +31,10 @@ + #include "dtb.hpp" + + PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", ++ // Short description. ++ "An implementation of the Dual-Tree Boruvka algorithm for computing the " ++ "Euclidean minimum spanning tree of a set of input points.", ++ // Long description. + "This program can compute the Euclidean minimum spanning tree of a set of " + "input points using the dual-tree Boruvka algorithm." + "\n\n" +@@ -56,7 +60,14 @@ PROGRAM_INFO("Fast Euclidean Minimum Spanning Tree", + "The output matrix is a three-dimensional matrix, where each row indicates " + "an edge. The first dimension corresponds to the lesser index of the edge;" + " the second dimension corresponds to the greater index of the edge; and " +- "the third column corresponds to the distance between the two points."); ++ "the third column corresponds to the distance between the two points.", ++ SEE_ALSO("EMST Tutorial", "@doxygen/emst_tutorial.html"), ++ SEE_ALSO("Minimum spanning tree on Wikipedia", ++ "https://en.wikipedia.org/wiki/Minimum_spanning_tree"), ++ SEE_ALSO("Fast Euclidean Minimum Spanning Tree: Algorithm, Analysis, and " ++ "Applications (pdf)", "http://www.mlpack.org/papers/emst.pdf"), ++ SEE_ALSO("mlpack::emst::DualTreeBoruvka class documentation", ++ "@doxygen/classmlpack_1_1emst_1_1DualTreeBoruvka.html")); + + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); + PARAM_MATRIX_OUT("output", "Output data. Stored as an edge list.", "o"); +diff --git a/src/mlpack/methods/fastmks/CMakeLists.txt b/src/mlpack/methods/fastmks/CMakeLists.txt +index d9a6a6a8e..e6b6e55c1 100644 +--- a/src/mlpack/methods/fastmks/CMakeLists.txt ++++ b/src/mlpack/methods/fastmks/CMakeLists.txt +@@ -22,3 +22,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(fastmks) + add_python_binding(fastmks) ++add_markdown_docs(fastmks "cli;python" "geometry") +diff --git a/src/mlpack/methods/fastmks/fastmks_main.cpp b/src/mlpack/methods/fastmks/fastmks_main.cpp +index 1d8e7e7fb..fea273d65 100644 +--- a/src/mlpack/methods/fastmks/fastmks_main.cpp ++++ b/src/mlpack/methods/fastmks/fastmks_main.cpp +@@ -25,6 +25,12 @@ using namespace mlpack::metric; + using namespace mlpack::util; + + PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", ++ // Short description. ++ "An implementation of the single-tree and dual-tree fast max-kernel search" ++ " (FastMKS) algorithm. Given a set of reference points and a set of query" ++ " points, this can find the reference point with maximum kernel value for " ++ "each query point; trained models can be reused for future queries.", ++ // Long description. + "This program will find the k maximum kernels of a set of points, " + "using a query set and a reference set (which can optionally be the same " + "set). More specifically, for each point in the query set, the k points in" +@@ -51,7 +57,14 @@ PROGRAM_INFO("FastMKS (Fast Max-Kernel Search)", + "\n\n" + "This program performs FastMKS using a cover tree. The base used to build " + "the cover tree can be specified with the " + PRINT_PARAM_STRING("base") + +- " parameter."); ++ " parameter.", ++ SEE_ALSO("Fast max-kernel search tutorial (fastmks)", ++ "@doxygen/fmkstutorial.html"), ++ SEE_ALSO("k-nearest-neighbor search", "#knn"), ++ SEE_ALSO("Dual-tree Fast Exact Max-Kernel Search (pdf)", ++ "http://mlpack.org/papers/fmks.pdf"), ++ SEE_ALSO("mlpack::fastmks::FastMKS class documentation", ++ "@doxygen/classmlpack_1_1fastmks_1_1FastMKS.html")); + + // Model-building parameters. + PARAM_MATRIX_IN("reference", "The reference dataset.", "r"); +diff --git a/src/mlpack/methods/gmm/CMakeLists.txt b/src/mlpack/methods/gmm/CMakeLists.txt +index 74f9e8ace..964f490be 100644 +--- a/src/mlpack/methods/gmm/CMakeLists.txt ++++ b/src/mlpack/methods/gmm/CMakeLists.txt +@@ -23,7 +23,12 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(gmm_train) + add_python_binding(gmm_train) ++add_markdown_docs(gmm_train "cli;python" "clustering") ++ + add_cli_executable(gmm_generate) + add_python_binding(gmm_generate) ++add_markdown_docs(gmm_generate "cli;python" "clustering") ++ + add_cli_executable(gmm_probability) + add_python_binding(gmm_probability) ++add_markdown_docs(gmm_probability "cli;python" "clustering") +diff --git a/src/mlpack/methods/gmm/gmm_generate_main.cpp b/src/mlpack/methods/gmm/gmm_generate_main.cpp +index 784234941..4f3d10fdb 100644 +--- a/src/mlpack/methods/gmm/gmm_generate_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_generate_main.cpp +@@ -20,6 +20,10 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Sample Generator", ++ // Short description. ++ "A sample generator for pre-trained GMMs. Given a pre-trained GMM, this " ++ "can sample new points randomly from that distribution.", ++ // Long description. + "This program is able to generate samples from a pre-trained GMM (use " + "gmm_train to train a GMM). The pre-trained GMM must be specified with " + "the " + PRINT_PARAM_STRING("input_model") + " parameter. The number " +@@ -32,7 +36,13 @@ PROGRAM_INFO("GMM Sample Generator", + "samples in " + PRINT_DATASET("samples") + ":" + "\n\n" + + PRINT_CALL("gmm_generate", "input_model", "gmm", "samples", 100, "output", +- "samples")); ++ "samples"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM model to generate samples " + "from.", "m"); +diff --git a/src/mlpack/methods/gmm/gmm_probability_main.cpp b/src/mlpack/methods/gmm/gmm_probability_main.cpp +index f40c86628..6ba29585c 100644 +--- a/src/mlpack/methods/gmm/gmm_probability_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_probability_main.cpp +@@ -20,6 +20,11 @@ using namespace mlpack::gmm; + using namespace mlpack::util; + + PROGRAM_INFO("GMM Probability Calculator", ++ // Short description. ++ "A probability calculator for GMMs. Given a pre-trained GMM and a set of " ++ "points, this can compute the probability that each point is from the given" ++ " GMM.", ++ // Long description. + "This program calculates the probability that given points came from a " + "given GMM (that is, P(X | gmm)). The GMM is specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and the points are " +@@ -33,7 +38,13 @@ PROGRAM_INFO("GMM Probability Calculator", + PRINT_DATASET("probs") + ", the following command could be used:" + "\n\n" + + PRINT_CALL("gmm_probability", "input_model", "gmm", "input", "points", +- "output", "probs")); ++ "output", "probs"), ++ SEE_ALSO("@gmm_train", "#gmm_train"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + PARAM_MODEL_IN_REQ(GMM, "input_model", "Input GMM to use as model.", "m"); + PARAM_MATRIX_IN_REQ("input", "Input matrix to calculate probabilities of.", +diff --git a/src/mlpack/methods/gmm/gmm_train_main.cpp b/src/mlpack/methods/gmm/gmm_train_main.cpp +index c331d04dc..a01adfcb0 100644 +--- a/src/mlpack/methods/gmm/gmm_train_main.cpp ++++ b/src/mlpack/methods/gmm/gmm_train_main.cpp +@@ -26,6 +26,11 @@ using namespace mlpack::kmeans; + using namespace std; + + PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", ++ // Short description. ++ "An implementation of the EM algorithm for training Gaussian mixture " ++ "models (GMMs). Given a dataset, this can train a GMM for future use " ++ "with other tools.", ++ // Long description. + "This program takes a parametric estimate of a Gaussian mixture model (GMM)" + " using the EM algorithm to find the maximum likelihood estimate. The " + "model may be saved and reused by other mlpack GMM tools." +@@ -84,7 +89,13 @@ PROGRAM_INFO("Gaussian Mixture Model (GMM) Training", + ", the following command may be used: " + "\n\n" + + PRINT_CALL("gmm_train", "input_model", "gmm", "input", "data2", +- "gaussians", 6, "output_model", "new_gmm")); ++ "gaussians", 6, "output_model", "new_gmm"), ++ SEE_ALSO("@gmm_generate", "#gmm_generate"), ++ SEE_ALSO("@gmm_probability", "#gmm_probability"), ++ SEE_ALSO("Gaussian Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mixture_model#Gaussian_mixture_model"), ++ SEE_ALSO("mlpack::gmm::GMM class documentation", ++ "@doxygen/classmlpack_1_1gmm_1_1GMM.html")); + + // Parameters for training. + PARAM_MATRIX_IN_REQ("input", "The training data on which the model will be " +diff --git a/src/mlpack/methods/hmm/CMakeLists.txt b/src/mlpack/methods/hmm/CMakeLists.txt +index 7add2baa3..7247430bb 100644 +--- a/src/mlpack/methods/hmm/CMakeLists.txt ++++ b/src/mlpack/methods/hmm/CMakeLists.txt +@@ -21,9 +21,16 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hmm_train) + add_python_binding(hmm_train) ++add_markdown_docs(hmm_train "cli;python" "misc. / other") ++ + add_cli_executable(hmm_loglik) + add_python_binding(hmm_loglik) ++add_markdown_docs(hmm_loglik "cli;python" "misc. / other") ++ + add_cli_executable(hmm_viterbi) + add_python_binding(hmm_viterbi) ++add_markdown_docs(hmm_viterbi "cli;python" "misc. / other") ++ + add_cli_executable(hmm_generate) + add_python_binding(hmm_generate) ++add_markdown_docs(hmm_generate "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/hmm/hmm_generate_main.cpp b/src/mlpack/methods/hmm/hmm_generate_main.cpp +index 0efc8dc84..cbfbe30bf 100644 +--- a/src/mlpack/methods/hmm/hmm_generate_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_generate_main.cpp +@@ -29,8 +29,13 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " +- "utility takes an already-trained HMM, specified as the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", ++ // Short description. ++ "A utility to generate random sequences from a pre-trained Hidden Markov " ++ "Model (HMM). The length of the desired sequence can be specified, and a " ++ "random sequence of observations is returned.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as the " + + PRINT_PARAM_STRING("model") + " parameter, and generates a random " + "observation sequence and hidden state sequence based on its parameters. " + "The observation sequence may be saved with the " + +@@ -47,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Generator", "This " + PRINT_DATASET("states") + ", the following command may be used: " + "\n\n" + + PRINT_CALL("hmm_generate", "model", "hmm", "length", 150, "output", +- "observations", "state", "states")); ++ "observations", "state", "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MODEL_IN_REQ(HMMModel, "model", "Trained HMM to generate sequences with.", + "m"); +diff --git a/src/mlpack/methods/hmm/hmm_loglik_main.cpp b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +index 6ebb3cbef..23a482603 100644 +--- a/src/mlpack/methods/hmm/hmm_loglik_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_loglik_main.cpp +@@ -26,8 +26,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " +- "utility takes an already-trained HMM, specified with the " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", ++ // Short description. ++ "A utility for computing the log-likelihood of a sequence for Hidden Markov" ++ " Models (HMMs). Given a pre-trained HMM and an observation sequence, this" ++ " computes and returns the log-likelihood of that sequence being observed " ++ "from that HMM.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified with the " + + PRINT_PARAM_STRING("input_model") + " parameter, and evaluates the " + "log-likelihood of a sequence of observations, given with the " + + PRINT_PARAM_STRING("input") + " parameter. The computed log-likelihood is" +@@ -37,7 +43,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Sequence Log-Likelihood", "This " + PRINT_DATASET("seq") + " with the pre-trained HMM " + PRINT_MODEL("hmm") + + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm")); ++ PRINT_CALL("hmm_loglik", "input", "seq", "input_model", "hmm"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "File containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "File containing HMM.", "m"); +diff --git a/src/mlpack/methods/hmm/hmm_train_main.cpp b/src/mlpack/methods/hmm/hmm_train_main.cpp +index 109cfbddd..3e21085a1 100644 +--- a/src/mlpack/methods/hmm/hmm_train_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_train_main.cpp +@@ -27,9 +27,15 @@ using namespace mlpack::math; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " +- "Hidden Markov Model to be trained on labeled or unlabeled data. It " +- "support three types of HMMs: discrete HMMs, Gaussian HMMs, or GMM HMMs." ++PROGRAM_INFO("Hidden Markov Model (HMM) Training", ++ // Short description. ++ "An implementation of training algorithms for Hidden Markov Models (HMMs). " ++ "Given labeled or unlabeled data, an HMM can be trained for further use " ++ "with other mlpack HMM tools.", ++ // Long description. ++ "This program allows a Hidden Markov Model to be trained on labeled or " ++ "unlabeled data. It supports three types of HMMs: discrete HMMs, " ++ "Gaussian HMMs, or GMM HMMs." + "\n\n" + "Either one input sequence can be specified (with --input_file), or, a " + "file containing files in which input sequences can be found (when " +@@ -46,7 +52,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Training", "This program allows a " + "\n\n" + "Optionally, a pre-created HMM model can be used as a guess for the " + "transition matrix and emission probabilities; this is specifiable with " +- "--model_file."); ++ "--model_file.", ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("@hmm_viterbi", "#hmm_viterbi"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_STRING_IN_REQ("input_file", "File containing input observations.", "i"); + PARAM_STRING_IN("type", "Type of HMM: discrete | gaussian | gmm.", "t", +diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +index d0e1168b3..1fa3c5f58 100644 +--- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp ++++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +@@ -27,8 +27,14 @@ using namespace mlpack::gmm; + using namespace arma; + using namespace std; + +-PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " +- "utility takes an already-trained HMM, specified as " + ++PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", ++ // Short description. ++ "A utility for computing the most probable hidden state sequence for Hidden" ++ " Markov Models (HMMs). Given a pre-trained HMM and an observed sequence, " ++ "this uses the Viterbi algorithm to compute and return the most probable " ++ "hidden state sequence.", ++ // Long description. ++ "This utility takes an already-trained HMM, specified as " + + PRINT_PARAM_STRING("input_model") + ", and evaluates the most probable " + "hidden state sequence of a given sequence of observations (specified as " + "'" + PRINT_PARAM_STRING("input") + ", using the Viterbi algorithm. The " +@@ -41,7 +47,14 @@ PROGRAM_INFO("Hidden Markov Model (HMM) Viterbi State Prediction", "This " + ", the following command could be used:" + "\n\n" + + PRINT_CALL("hmm_viterbi", "input", "obs", "input_model", "hmm", "output", +- "states")); ++ "states"), ++ SEE_ALSO("@hmm_train", "#hmm_train"), ++ SEE_ALSO("@hmm_generate", "#hmm_generate"), ++ SEE_ALSO("@hmm_loglik", "#hmm_loglik"), ++ SEE_ALSO("Hidden Mixture Models on Wikipedia", ++ "https://en.wikipedia.org/wiki/Hidden_Markov_model"), ++ SEE_ALSO("mlpack::hmm::HMM class documentation", ++ "@doxygen/classmlpack_1_1hmm_1_1HMM.html")); + + PARAM_MATRIX_IN_REQ("input", "Matrix containing observations,", "i"); + PARAM_MODEL_IN_REQ(HMMModel, "input_model", "Trained HMM to use.", "m"); +diff --git a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +index b573a19c6..2a94415b3 100644 +--- a/src/mlpack/methods/hoeffding_trees/CMakeLists.txt ++++ b/src/mlpack/methods/hoeffding_trees/CMakeLists.txt +@@ -30,3 +30,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(hoeffding_tree) + add_python_binding(hoeffding_tree) ++add_markdown_docs(hoeffding_tree "cli;python" "classification") +diff --git a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +index 46437b4ad..45b58713c 100644 +--- a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp ++++ b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +@@ -26,6 +26,12 @@ using namespace mlpack::data; + using namespace mlpack::util; + + PROGRAM_INFO("Hoeffding trees", ++ // Short description. ++ "An implementation of Hoeffding trees, a form of streaming decision tree " ++ "for classification. Given labeled data, a Hoeffding tree can be trained " ++ "and saved for later use, or a pre-trained Hoeffding tree can be used for " ++ "predicting the classifications of new points.", ++ // Long description. + "This program implements Hoeffding trees, a form of streaming decision tree" + " suited best for large (or streaming) datasets. This program supports " + "both categorical and numeric data. Given an input dataset, this program " +@@ -70,7 +76,13 @@ PROGRAM_INFO("Hoeffding trees", + PRINT_DATASET("class_probs") + " with the following command: " + "\n\n" + + PRINT_CALL("hoeffding_tree", "input_model", "tree", "test", "test_set", +- "predictions", "predictions", "probabilities", "class_probs")); ++ "predictions", "predictions", "probabilities", "class_probs"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Mining High-Speed Data Streams (pdf)", ++ "http://dm.cs.washington.edu/papers/vfdt-kdd00.pdf"), ++ SEE_ALSO("mlpack::tree::HoeffdingTree class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1HoeffdingTree.html")); + + PARAM_MATRIX_AND_INFO_IN("training", "Training dataset (may be categorical).", + "t"); +diff --git a/src/mlpack/methods/kernel_pca/CMakeLists.txt b/src/mlpack/methods/kernel_pca/CMakeLists.txt +index c14a9f3ec..9bd0cd3a0 100644 +--- a/src/mlpack/methods/kernel_pca/CMakeLists.txt ++++ b/src/mlpack/methods/kernel_pca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kernel_pca) + add_python_binding(kernel_pca) ++add_markdown_docs(kernel_pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +index a9b3478e0..a736c27ed 100644 +--- a/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp ++++ b/src/mlpack/methods/kernel_pca/kernel_pca_main.cpp +@@ -41,6 +41,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Kernel Principal Components Analysis", ++ // Short description. ++ "An implementation of Kernel Principal Components Analysis (KPCA). This " ++ "can be used to perform nonlinear dimensionality reduction or preprocessing" ++ " on a given dataset.", ++ // Long description. + "This program performs Kernel Principal Components Analysis (KPCA) on the " + "specified dataset with the specified kernel. This will transform the " + "data onto the kernel principal components, and optionally reduce the " +@@ -93,7 +98,13 @@ PROGRAM_INFO("Kernel Principal Components Analysis", + "the kernel matrix; to specify the sampling scheme, the " + + PRINT_PARAM_STRING("sampling") + " parameter is used. The " + "sampling scheme for the Nystr\u00F6m method can be chosen from the " +- "following list: 'kmeans', 'random', 'ordered'."); ++ "following list: 'kmeans', 'random', 'ordered'.", ++ SEE_ALSO("Kernel principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Kernel_principal_component_analysis"), ++ SEE_ALSO("Kernel Principal Component Analysis (pdf)", ++ "http://pca.narod.ru/scholkopf_kernel.pdf"), ++ SEE_ALSO("mlpack::kpca::KernelPCA class documentation", ++ "@doxygen/classmlpack_1_1kpca_1_1KernelPCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform KPCA on.", "i"); + PARAM_MATRIX_OUT("output", "Matrix to save modified dataset to.", "o"); +@@ -107,9 +118,9 @@ PARAM_INT_IN("new_dimensionality", "If not 0, reduce the dimensionality of " + PARAM_FLAG("center", "If set, the transformed data will be centered about the " + "origin.", "c"); + +-PARAM_FLAG("nystroem_method", "If set, the nystroem method will be used.", "n"); ++PARAM_FLAG("nystroem_method", "If set, the Nystroem method will be used.", "n"); + +-PARAM_STRING_IN("sampling", "Sampling scheme to use for the nystroem method: " ++PARAM_STRING_IN("sampling", "Sampling scheme to use for the Nystroem method: " + "'kmeans', 'random', 'ordered'", "s", "kmeans"); + + PARAM_DOUBLE_IN("kernel_scale", "Scale, for 'hyptan' kernel.", "S", 1.0); +diff --git a/src/mlpack/methods/kmeans/CMakeLists.txt b/src/mlpack/methods/kmeans/CMakeLists.txt +index bf4990083..9cfec0fdc 100644 +--- a/src/mlpack/methods/kmeans/CMakeLists.txt ++++ b/src/mlpack/methods/kmeans/CMakeLists.txt +@@ -40,3 +40,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(kmeans) + add_python_binding(kmeans) ++add_markdown_docs(kmeans "cli;python" "clustering") +diff --git a/src/mlpack/methods/kmeans/kmeans_main.cpp b/src/mlpack/methods/kmeans/kmeans_main.cpp +index b0319ccb5..9d3f40c7d 100644 +--- a/src/mlpack/methods/kmeans/kmeans_main.cpp ++++ b/src/mlpack/methods/kmeans/kmeans_main.cpp +@@ -28,11 +28,17 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " +- "on the given dataset. It can return the learned cluster assignments, and " +- "the centroids of the clusters. Empty clusters are not allowed by default;" +- " when a cluster becomes empty, the point furthest from the centroid of the" +- " cluster with maximum variance is taken to fill that cluster." ++PROGRAM_INFO("K-Means Clustering", ++ // Short description. ++ "An implementation of several strategies for efficient k-means clustering. " ++ "Given a dataset and a value of k, this computes and returns a k-means " ++ "clustering on that data.", ++ // Long description. ++ "This program performs K-Means clustering on the given dataset. It can " ++ "return the learned cluster assignments, and the centroids of the clusters." ++ " Empty clusters are not allowed by default; when a cluster becomes empty," ++ " the point furthest from the centroid of the cluster with maximum variance" ++ " is taken to fill that cluster." + "\n\n" + "Optionally, the Bradley and Fayyad approach (\"Refining initial points for" + " k-means clustering\", 1998) can be used to select initial points by " +@@ -85,7 +91,21 @@ PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " + "following command may be used:" + "\n\n" + + PRINT_CALL("kmeans", "input", "data", "initial_centroids", "initial", +- "clusters", 10, "max_iterations", 500, "centroid", "final")); ++ "clusters", 10, "max_iterations", 500, "centroid", "final"), ++ SEE_ALSO("K-Means tutorial", "@doxygen/kmtutorial.html"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Using the triangle inequality to accelerate k-means (pdf)", ++ "http://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf"), ++ SEE_ALSO("Making k-means even faster (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.586.2554" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("Accelerating exact k-means algorithms with geometric reasoning " ++ "(pdf)", "http://reports-archive.adm.cs.cmu.edu/anon/anon/usr/ftp/" ++ "usr0/ftp/2000/CMU-CS-00-105.pdf"), ++ SEE_ALSO("A dual-tree algorithm for fast k-means clustering with large k " ++ "(pdf)", "http://www.ratml.org/pub/pdf/2017dual.pdf"), ++ SEE_ALSO("mlpack::kmeans::KMeans class documentation", ++ "@doxygen/classmlpack_1_1kmeans_1_1KMeans.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/lars/CMakeLists.txt b/src/mlpack/methods/lars/CMakeLists.txt +index 1388f24be..7e4cb7890 100644 +--- a/src/mlpack/methods/lars/CMakeLists.txt ++++ b/src/mlpack/methods/lars/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(lars) + add_python_binding(lars) ++add_markdown_docs(lars "cli;python" "regression") +diff --git a/src/mlpack/methods/lars/lars_main.cpp b/src/mlpack/methods/lars/lars_main.cpp +index 040c5f0c3..e0b2f38f3 100644 +--- a/src/mlpack/methods/lars/lars_main.cpp ++++ b/src/mlpack/methods/lars/lars_main.cpp +@@ -21,10 +21,16 @@ using namespace mlpack; + using namespace mlpack::regression; + using namespace mlpack::util; + +-PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " +- "(Stagewise/laSso). This is a stage-wise homotopy-based algorithm for " +- "L1-regularized linear regression (LASSO) and L1+L2-regularized linear " +- "regression (Elastic Net)." ++PROGRAM_INFO("LARS", ++ // Short description. ++ "An implementation of Least Angle Regression (Stagewise/laSso), also known" ++ " as LARS. This can train a LARS/LASSO/Elastic Net model and use that " ++ "model or a pre-trained model to output regression predictions for a test " ++ "set.", ++ // Long description. ++ "An implementation of LARS: Least Angle Regression (Stagewise/laSso). " ++ "This is a stage-wise homotopy-based algorithm for L1-regularized linear " ++ "regression (LASSO) and L1+L2-regularized linear regression (Elastic Net)." + "\n\n" + "This program is able to train a LARS/LASSO/Elastic Net model or load a " + "model from file, output regression predictions for a test set, and save " +@@ -80,7 +86,12 @@ PROGRAM_INFO("LARS", "An implementation of LARS: Least Angle Regression " + "and save those responses to " + PRINT_DATASET("test_predictions") + ": " + "\n\n" + + PRINT_CALL("lars", "input_model", "lasso_model", "test", "test", +- "output_predictions", "test_predictions")); ++ "output_predictions", "test_predictions"), ++ SEE_ALSO("@linear_regression", "#linear_regression"), ++ SEE_ALSO("Least angle regression (pdf)", ++ "http://mlpack.org/papers/lars.pdf"), ++ SEE_ALSO("mlpack::regression::LARS C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LARS.html")); + + PARAM_TMATRIX_IN("input", "Matrix of covariates (X).", "i"); + PARAM_MATRIX_IN("responses", "Matrix of responses/observations (y).", "r"); +diff --git a/src/mlpack/methods/linear_regression/CMakeLists.txt b/src/mlpack/methods/linear_regression/CMakeLists.txt +index 58a7426e8..b4e63cdca 100644 +--- a/src/mlpack/methods/linear_regression/CMakeLists.txt ++++ b/src/mlpack/methods/linear_regression/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(linear_regression) + add_python_binding(linear_regression) ++add_markdown_docs(linear_regression "cli;python" "regression") +diff --git a/src/mlpack/methods/linear_regression/linear_regression_main.cpp b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +index c1e33cd06..1cb58a85c 100644 +--- a/src/mlpack/methods/linear_regression/linear_regression_main.cpp ++++ b/src/mlpack/methods/linear_regression/linear_regression_main.cpp +@@ -22,6 +22,12 @@ using namespace arma; + using namespace std; + + PROGRAM_INFO("Simple Linear Regression and Prediction", ++ // Short description. ++ "An implementation of simple linear regression and ridge regression using " ++ "ordinary least squares. Given a dataset and responses, a model can be " ++ "trained and saved for later use, or a pre-trained model can be used to " ++ "output regression predictions for a test set.", ++ // Long description. + "An implementation of simple linear regression and simple ridge regression " + "using ordinary least squares. This solves the problem" + "\n\n" +@@ -63,7 +69,13 @@ PROGRAM_INFO("Simple Linear Regression and Prediction", + "used:" + "\n\n" + + PRINT_CALL("linear_regression", "input_model", "lr_model", "test", "X_test", +- "output_predictions", "X_test_responses")); ++ "output_predictions", "X_test_responses"), ++ SEE_ALSO("Linear/ridge regression tutorial", "@doxygen/lrtutorial.html"), ++ SEE_ALSO("@lars", "#lars"), ++ SEE_ALSO("Linear regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Linear_regression"), ++ SEE_ALSO("mlpack::regression::LinearRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LinearRegression.html")); + + PARAM_MATRIX_IN("training", "Matrix containing training set X (regressors).", + "t"); +diff --git a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +index 6720525c7..d4b498e55 100644 +--- a/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt ++++ b/src/mlpack/methods/local_coordinate_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(local_coordinate_coding) + add_python_binding(local_coordinate_coding) ++add_markdown_docs(local_coordinate_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +index e0b8c2964..0925382bc 100644 +--- a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp ++++ b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +@@ -24,6 +24,12 @@ using namespace mlpack::sparse_coding; // For NothingInitializer. + using namespace mlpack::util; + + PROGRAM_INFO("Local Coordinate Coding", ++ // Short description. ++ "An implementation of Local Coordinate Coding (LCC), a data transformation " ++ "technique. Given input data, this transforms each point to be expressed " ++ "as a linear combination of a few points in the dataset; once an LCC model " ++ "is trained, it can be used to transform points later also.", ++ // Long description. + "An implementation of Local Coordinate Coding (LCC), which " + "codes data that approximately lives on a manifold using a variation of l1-" + "norm regularized sparse coding. Given a dense data matrix X with n points" +@@ -67,7 +73,13 @@ PROGRAM_INFO("Local Coordinate Coding", + "be used:" + "\n\n" + + PRINT_CALL("local_coordinate_coding", "input_model", "lcc_model", "test", +- "points", "codes", "new_codes")); ++ "points", "codes", "new_codes"), ++ SEE_ALSO("@sparse_coding", "#sparse_coding"), ++ SEE_ALSO("Nonlinear learning using local coordinate coding (pdf)", ++ "https://papers.nips.cc/paper/3875-nonlinear-learning-using-local-" ++ "coordinate-coding.pdf"), ++ SEE_ALSO("mlpack::lcc::LocalCoordinateCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1lcc_1_1LocalCoordinateCoding.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +diff --git a/src/mlpack/methods/logistic_regression/CMakeLists.txt b/src/mlpack/methods/logistic_regression/CMakeLists.txt +index d7edd6ba1..673e214c6 100644 +--- a/src/mlpack/methods/logistic_regression/CMakeLists.txt ++++ b/src/mlpack/methods/logistic_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(logistic_regression) + add_python_binding(logistic_regression) ++add_markdown_docs(logistic_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +index 2a0ed23a7..385525ab0 100644 +--- a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp ++++ b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +@@ -24,6 +24,11 @@ using namespace mlpack::optimization; + using namespace mlpack::util; + + PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", ++ // Short description. ++ "An implementation of L2-regularized logistic regression for two-class " ++ "classification. Given labeled data, a model can be trained and saved for " ++ "future use; or, a pre-trained model can be used to classify new points.", ++ // Long description. + "An implementation of L2-regularized logistic regression using either the " + "L-BFGS optimizer or SGD (stochastic gradient descent). This solves the " + "regression problem" +@@ -39,8 +44,8 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + "those things at once. In addition, this program allows classification on " + "a test dataset (specified with the " + PRINT_PARAM_STRING("test") + " " + "parameter) and the classification results may be saved with the " + +- PRINT_PARAM_STRING("output") + " output parameter. The trained logistic " +- "regression model may be saved using the " + ++ PRINT_PARAM_STRING("output") + " output parameter." ++ " The trained logistic regression model may be saved using the " + + PRINT_PARAM_STRING("output_model") + " output parameter." + "\n\n" + "The training data, if specified, may have class labels as its last " +@@ -95,7 +100,13 @@ PROGRAM_INFO("L2-regularized Logistic Regression and Prediction", + PRINT_DATASET("predictions") + "', the following command may be used: " + "\n\n" + + PRINT_CALL("logistic_regression", "input_model", "lr_model", "test", "test", +- "output", "predictions")); ++ "output", "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Logistic regression on Wikipedia", ++ "https://en.wikipedia.org/wiki/Logistic_regression"), ++ SEE_ALSO("mlpack::regression::LogisticRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1LogisticRegression.html")); + + // Training parameters. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/lsh/CMakeLists.txt b/src/mlpack/methods/lsh/CMakeLists.txt +index 758a2152b..4ec7baacf 100644 +--- a/src/mlpack/methods/lsh/CMakeLists.txt ++++ b/src/mlpack/methods/lsh/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # sets with p-stable LSH. + add_cli_executable(lsh) + add_python_binding(lsh) ++add_markdown_docs(lsh "cli;python" "geometry") +diff --git a/src/mlpack/methods/lsh/lsh_main.cpp b/src/mlpack/methods/lsh/lsh_main.cpp +index d9923e02b..c082e10aa 100644 +--- a/src/mlpack/methods/lsh/lsh_main.cpp ++++ b/src/mlpack/methods/lsh/lsh_main.cpp +@@ -25,6 +25,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", ++ // Short description. ++ "An implementation of approximate k-nearest-neighbor search with " ++ "locality-sensitive hashing (LSH). Given a set of reference points and a " ++ "set of query points, this will compute the k approximate nearest neighbors" ++ " of each query point in the reference set; models can be saved for future " ++ "use.", ++ // Long description. + "This program will calculate the k approximate-nearest-neighbors of a set " + "of points using locality-sensitive hashing. You may specify a separate set" + " of reference points and query points, or just a reference set which will " +@@ -49,7 +56,15 @@ PROGRAM_INFO("K-Approximate-Nearest-Neighbor Search with LSH", + " parameter can be specified to set the random seed." + "\n\n" + "This program also has many other parameters to control its functionality;" +- " see the parameter-specific documentation for more information."); ++ " see the parameter-specific documentation for more information.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("Locality-sensitive hashing on Wikipedia", ++ "https://en.wikipedia.org/wiki/Locality-sensitive_hashing"), ++ SEE_ALSO("Locality-sensitive hashing scheme based on p-stable distributions" ++ " (pdf)", "http://mlpack.org/papers/lsh.pdf"), ++ SEE_ALSO("mlpack::neighbor::LSHSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1LSHSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/mean_shift/CMakeLists.txt b/src/mlpack/methods/mean_shift/CMakeLists.txt +index 1087ab532..5aced5225 100644 +--- a/src/mlpack/methods/mean_shift/CMakeLists.txt ++++ b/src/mlpack/methods/mean_shift/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(mean_shift) + add_python_binding(mean_shift) ++add_markdown_docs(mean_shift "cli;python" "clustering") +diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +index 4710ccaad..ac7f17e18 100644 +--- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp ++++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp +@@ -23,9 +23,15 @@ using namespace mlpack::util; + using namespace std; + + // Define parameters for the executable. +-PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " +- "clustering on the given dataset, storing the learned cluster assignments " +- "either as a column of labels in the input dataset or separately." ++PROGRAM_INFO("Mean Shift Clustering", ++ // Short description. ++ "A fast implementation of mean-shift clustering using dual-tree range " ++ "search. Given a dataset, this uses the mean shift algorithm to produce " ++ "and return a clustering of the data.", ++ // Long description. ++ "This program performs mean shift clustering on the given dataset, storing " ++ "the learned cluster assignments either as a column of labels in the input " ++ "dataset or separately." + "\n\n" + "The input dataset should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter, and the radius used for search" +@@ -42,7 +48,16 @@ PROGRAM_INFO("Mean Shift Clustering", "This program performs mean shift " + PRINT_DATASET("data") + " and store the centroids to " + + PRINT_DATASET("centroids") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids")); ++ PRINT_CALL("mean_shift", "input", "data", "centroid", "centroids"), ++ SEE_ALSO("@kmeans", "#kmeans"), ++ SEE_ALSO("@dbscan", "#dbscan"), ++ SEE_ALSO("Mean shift on Wikipedia", ++ "https://en.wikipedia.org/wiki/Mean_shift"), ++ SEE_ALSO("Mean Shift, Mode Seeking, and Clustering (pdf)", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.510.1222" ++ "&rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::mean_shift::MeanShift C++ class documentation", ++ "@doxygen/classmlpack_1_1meanshift_1_1MeanShift.html")); + + // Required options. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform clustering on.", "i"); +diff --git a/src/mlpack/methods/naive_bayes/CMakeLists.txt b/src/mlpack/methods/naive_bayes/CMakeLists.txt +index f15dc229c..b2afe0707 100644 +--- a/src/mlpack/methods/naive_bayes/CMakeLists.txt ++++ b/src/mlpack/methods/naive_bayes/CMakeLists.txt +@@ -16,3 +16,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nbc) + add_python_binding(nbc) ++add_markdown_docs(nbc "cli;python" "classification") +diff --git a/src/mlpack/methods/naive_bayes/nbc_main.cpp b/src/mlpack/methods/naive_bayes/nbc_main.cpp +index 68a4a70e0..612d675d0 100644 +--- a/src/mlpack/methods/naive_bayes/nbc_main.cpp ++++ b/src/mlpack/methods/naive_bayes/nbc_main.cpp +@@ -26,6 +26,11 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Parametric Naive Bayes Classifier", ++ // Short description. ++ "An implementation of the Naive Bayes Classifier, used for classification. " ++ "Given labeled data, an NBC model can be trained and saved, or, a " ++ "pre-trained model can be used for classification.", ++ // Long description. + "This program trains the Naive Bayes classifier on the given labeled " + "training set, or loads a model from the given model file, and then may use" + " that trained model to classify the points in a given test set." +@@ -66,7 +71,14 @@ PROGRAM_INFO("Parametric Naive Bayes Classifier", + "may be used:" + "\n\n" + + PRINT_CALL("nbc", "input_model", "nbc_model", "test", "test_set", "output", +- "predictions")); ++ "predictions"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Naive Bayes classifier on Wikipedia", ++ "https://en.wikipedia.org/wiki/Naive_Bayes_classifier"), ++ SEE_ALSO("mlpack::naive_bayes::NaiveBayesClassifier C++ class " ++ "documentation", "@doxygen/classmlpack_1_1naive__bayes_1_1" ++ "NaiveBayesClassifier.html")); + + // A struct for saving the model with mappings. + struct NBCModel +diff --git a/src/mlpack/methods/nca/CMakeLists.txt b/src/mlpack/methods/nca/CMakeLists.txt +index 777eef39b..7b1fd98f5 100644 +--- a/src/mlpack/methods/nca/CMakeLists.txt ++++ b/src/mlpack/methods/nca/CMakeLists.txt +@@ -18,3 +18,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(nca) + add_python_binding(nca) ++add_markdown_docs(nca "cli;python" "transformations") +diff --git a/src/mlpack/methods/nca/nca_main.cpp b/src/mlpack/methods/nca/nca_main.cpp +index b1dd431ae..e26cae5e7 100644 +--- a/src/mlpack/methods/nca/nca_main.cpp ++++ b/src/mlpack/methods/nca/nca_main.cpp +@@ -22,6 +22,12 @@ + + // Define parameters. + PROGRAM_INFO("Neighborhood Components Analysis (NCA)", ++ // Short description. ++ "An implementation of neighborhood components analysis, a distance learning" ++ " technique that can be used for preprocessing. Given a labeled dataset, " ++ "this uses NCA, which seeks to improve the k-nearest-neighbor " ++ "classification, and returns the learned distance metric.", ++ // Long description. + "This program implements Neighborhood Components Analysis, both a linear " + "dimensionality reduction technique and a distance learning technique. The" + " method seeks to improve k-nearest-neighbor classification on a dataset " +@@ -82,7 +88,14 @@ PROGRAM_INFO("Neighborhood Components Analysis (NCA)", + "mlpack L-BFGS documentation (in lbfgs.hpp) or the vast set of published " + "literature on L-BFGS." + "\n\n" +- "By default, the SGD optimizer is used."); ++ "By default, the SGD optimizer is used.", ++ SEE_ALSO("Neighbourhood components analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Neighbourhood_components_analysis"), ++ SEE_ALSO("Neighbourhood components analysis (pdf)", ++ "http://papers.nips.cc/paper/2566-neighbourhood-components-" ++ "analysis.pdf"), ++ SEE_ALSO("mlpack::nca::NCA C++ class documentation", ++ "@doxygen/classmlpack_1_1nca_1_1NCA.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset to run NCA on.", "i"); + PARAM_MATRIX_OUT("output", "Output matrix for learned distance matrix.", "o"); +diff --git a/src/mlpack/methods/neighbor_search/CMakeLists.txt b/src/mlpack/methods/neighbor_search/CMakeLists.txt +index 1958ebe17..789beb295 100644 +--- a/src/mlpack/methods/neighbor_search/CMakeLists.txt ++++ b/src/mlpack/methods/neighbor_search/CMakeLists.txt +@@ -29,5 +29,8 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # Add mlpack_knn and mlpack_kfn executables. + add_cli_executable(knn) + add_python_binding(knn) ++add_markdown_docs(knn "cli;python" "geometry") ++ + add_cli_executable(kfn) + add_python_binding(kfn) ++add_markdown_docs(kfn "cli;python" "geometry") +diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp +index 56415c3ae..57e2ffdf6 100644 +--- a/src/mlpack/methods/neighbor_search/kfn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp +@@ -34,6 +34,12 @@ typedef NSModel KFNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Furthest-Neighbors Search", ++ // Short description. ++ "An implementation of k-furthest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k furthest neighbors in the reference set of each query" ++ " point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-furthest-neighbors of a set of " + "points. You may specify a separate set of reference points and query " + "points, or just a reference set which will be used as both the reference " +@@ -51,7 +57,13 @@ PROGRAM_INFO("k-Furthest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th furthest neighbor from the point in the " + "query set with index i. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@approx_kfn", "#approx_kfn"), ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp +index 336315dea..46a2b53bf 100644 +--- a/src/mlpack/methods/neighbor_search/knn_main.cpp ++++ b/src/mlpack/methods/neighbor_search/knn_main.cpp +@@ -36,6 +36,12 @@ typedef NSModel KNNModel; + + // Information about the program itself. + PROGRAM_INFO("k-Nearest-Neighbors Search", ++ // Short description. ++ "An implementation of k-nearest-neighbor search using single-tree and " ++ "dual-tree algorithms. Given a set of reference points and query points, " ++ "this can find the k nearest neighbors in the reference set of each query " ++ "point using trees; trees that are built can be saved for future use.", ++ // Long description. + "This program will calculate the k-nearest-neighbors of a set of " + "points using kd-trees or cover trees (cover tree support is experimental " + "and may be slow). You may specify a separate set of " +@@ -53,7 +59,16 @@ PROGRAM_INFO("k-Nearest-Neighbors Search", + "neighbors output matrix corresponds to the index of the point in the " + "reference set which is the j'th nearest neighbor from the point in the " + "query set with index i. Row j and column i in the distances output matrix" +- " corresponds to the distance between those two points."); ++ " corresponds to the distance between those two points.", ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("@krann", "#krann"), ++ SEE_ALSO("@kfn", "#kfn"), ++ SEE_ALSO("NeighborSearch tutorial (k-nearest-neighbors)", ++ "@doxygen/nstutorial.html"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::neighbor::NeighborSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1NeighborSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/nmf/CMakeLists.txt b/src/mlpack/methods/nmf/CMakeLists.txt +index 18e01f1b4..f1c4666f3 100644 +--- a/src/mlpack/methods/nmf/CMakeLists.txt ++++ b/src/mlpack/methods/nmf/CMakeLists.txt +@@ -1,2 +1,3 @@ + add_cli_executable(nmf) + add_python_binding(nmf) ++add_markdown_docs(nmf "cli;python" "misc. / other") +diff --git a/src/mlpack/methods/nmf/nmf_main.cpp b/src/mlpack/methods/nmf/nmf_main.cpp +index e39273eb2..1e9e4a539 100644 +--- a/src/mlpack/methods/nmf/nmf_main.cpp ++++ b/src/mlpack/methods/nmf/nmf_main.cpp +@@ -28,10 +28,15 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " +- "non-negative matrix factorization on the given dataset, storing the " +- "resulting decomposed matrices in the specified files. For an input " +- "dataset V, NMF decomposes V into two matrices W and H such that " ++PROGRAM_INFO("Non-negative Matrix Factorization", ++ // Short description. ++ "An implementation of non-negative matrix factorization. This can be used " ++ "to decompose an input dataset into two low-rank non-negative components.", ++ // Long description. ++ "This program performs non-negative matrix factorization on the given " ++ "dataset, storing the resulting decomposed matrices in the specified " ++ "files. For an input dataset V, NMF decomposes V into two matrices W " ++ "and H such that " + "\n\n" + "V = W * H" + "\n\n" +@@ -60,7 +65,17 @@ PROGRAM_INFO("Non-negative Matrix Factorization", "This program performs " + PRINT_DATASET("H") + ", the following command could be used: " + "\n\n" + + PRINT_CALL("nmf", "input", "V", "w", "W", "h", "H", "rank", 10, +- "update_rules", "multdist")); ++ "update_rules", "multdist"), ++ SEE_ALSO("@cf", "#cf"), ++ SEE_ALSO("Alternating matrix factorization tutorial", ++ "@doxygen/amftutorial.html"), ++ SEE_ALSO("Non-negative matrix factorization on Wikipedia", ++ "https://en.wikipedia.org/wiki/Non-negative_matrix_factorization"), ++ SEE_ALSO("Algorithms for non-negative matrix factorization (pdf)", ++ "http://papers.nips.cc/paper/1861-algorithms-for-non-negative-matrix-" ++ "factorization.pdf"), ++ SEE_ALSO("mlpack::amf::AMF C++ class documentation", ++ "@doxygen/classmlpack_1_1amf_1_1AMF.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform NMF on.", "i"); +diff --git a/src/mlpack/methods/pca/CMakeLists.txt b/src/mlpack/methods/pca/CMakeLists.txt +index 097598ee8..8b317c4fc 100644 +--- a/src/mlpack/methods/pca/CMakeLists.txt ++++ b/src/mlpack/methods/pca/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(pca) + add_python_binding(pca) ++add_markdown_docs(pca "cli;python" "transformations") +diff --git a/src/mlpack/methods/pca/pca_main.cpp b/src/mlpack/methods/pca/pca_main.cpp +index 4176f0680..493a80026 100644 +--- a/src/mlpack/methods/pca/pca_main.cpp ++++ b/src/mlpack/methods/pca/pca_main.cpp +@@ -26,12 +26,18 @@ using namespace mlpack::util; + using namespace std; + + // Document program. +-PROGRAM_INFO("Principal Components Analysis", "This program performs principal " +- "components analysis on the given dataset using the exact, randomized, " +- "randomized block Krylov, or QUIC SVD method. It will transform the data " +- "onto its principal components, optionally performing dimensionality " +- "reduction by ignoring the principal components with the smallest " +- "eigenvalues." ++PROGRAM_INFO("Principal Components Analysis", ++ // Short description. ++ "An implementation of several strategies for principal components analysis " ++ "(PCA), a common preprocessing step. Given a dataset and a desired new " ++ "dimensionality, this can reduce the dimensionality of the data using the " ++ "linear transformation determined by PCA.", ++ // Long description. ++ "This program performs principal components analysis on the given dataset " ++ "using the exact, randomized, randomized block Krylov, or QUIC SVD method. " ++ "It will transform the data onto its principal components, optionally " ++ "performing dimensionality reduction by ignoring the principal components " ++ "with the smallest eigenvalues." + "\n\n" + "Use the " + PRINT_PARAM_STRING("input") + " parameter to specify the " + "dataset to perform PCA on. A desired new dimensionality can be specified " +@@ -52,7 +58,11 @@ PROGRAM_INFO("Principal Components Analysis", "This program performs principal " + PRINT_DATASET("data_mod") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("pca", "input", "data", "new_dimensionality", 5, +- "decomposition_method", "randomized", "output", "data_mod")); ++ "decomposition_method", "randomized", "output", "data_mod"), ++ SEE_ALSO("Principal component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Principal_component_analysis"), ++ SEE_ALSO("mlpack::pca::PCA C++ class documentation", ++ "@doxygen/classmlpack_1_1pca_1_1PCA.html")); + + // Parameters for program. + PARAM_MATRIX_IN_REQ("input", "Input dataset to perform PCA on.", "i"); +diff --git a/src/mlpack/methods/perceptron/CMakeLists.txt b/src/mlpack/methods/perceptron/CMakeLists.txt +index 8e6763464..9463f1139 100644 +--- a/src/mlpack/methods/perceptron/CMakeLists.txt ++++ b/src/mlpack/methods/perceptron/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(perceptron) + add_python_binding(perceptron) ++add_markdown_docs(perceptron "cli;python" "classification") +diff --git a/src/mlpack/methods/perceptron/perceptron_main.cpp b/src/mlpack/methods/perceptron/perceptron_main.cpp +index d23e6694b..5dde3939f 100644 +--- a/src/mlpack/methods/perceptron/perceptron_main.cpp ++++ b/src/mlpack/methods/perceptron/perceptron_main.cpp +@@ -26,6 +26,12 @@ using namespace std; + using namespace arma; + + PROGRAM_INFO("Perceptron", ++ // Short description. ++ "An implementation of a perceptron---a single level neural network--=for " ++ "classification. Given labeled data, a perceptron can be trained and saved" ++ " for future use; or, a pre-trained perceptron can be used for " ++ "classification on new points.", ++ // Long description. + "This program implements a perceptron, which is a single level neural " + "network. The perceptron makes its predictions based on a linear predictor " + "function combining a set of weights with the feature vector. The " +@@ -75,7 +81,12 @@ PROGRAM_INFO("Perceptron", + "cannot pass a perceptron model trained on 2 classes and then re-train with" + " a 4-class dataset. Similarly, attempting classification on a " + "3-dimensional dataset with a perceptron that has been trained on 8 " +- "dimensions will cause an error."); ++ "dimensions will cause an error.", ++ SEE_ALSO("@adaboost", "#adaboost"), ++ SEE_ALSO("Perceptron on Wikipedia", ++ "https://en.wikipedia.org/wiki/Perceptron"), ++ SEE_ALSO("mlpack::perceptron::Perceptron C++ class documentation", ++ "@doxygen/classmlpack_1_1perceptron_1_1Perceptron.html")); + + // When we save a model, we must also save the class mappings. So we use this + // auxiliary structure to store both the perceptron and the mapping, and we'll +diff --git a/src/mlpack/methods/preprocess/CMakeLists.txt b/src/mlpack/methods/preprocess/CMakeLists.txt +index af9299c64..a65487981 100644 +--- a/src/mlpack/methods/preprocess/CMakeLists.txt ++++ b/src/mlpack/methods/preprocess/CMakeLists.txt +@@ -15,10 +15,17 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + #add_cli_executable(preprocess_stats) + add_cli_executable(preprocess_split) + add_python_binding(preprocess_split) ++add_markdown_docs(preprocess_split "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_binarize) + add_python_binding(preprocess_binarize) ++add_markdown_docs(preprocess_binarize "cli;python" "preprocessing") ++ + add_cli_executable(preprocess_describe) + add_python_binding(preprocess_describe) ++add_markdown_docs(preprocess_describe "cli;python" "preprocessing") ++ + #add_cli_executable(preprocess_scan) + add_cli_executable(preprocess_imputer) + #add_python_binding(preprocess_imputer) ++add_markdown_docs(preprocess_imputer "cli" "preprocessing") +diff --git a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +index 53e6fb056..9d69834af 100644 +--- a/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_binarize_main.cpp +@@ -14,7 +14,13 @@ + #include + #include + +-PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " ++PROGRAM_INFO("Binarize Data", ++ // Short description. ++ "A utility to binarize a dataset. Given a dataset, this utility converts " ++ "each value in the desired dimension(s) to 0 or 1; this can be a useful " ++ "preprocessing step.", ++ // Long description. ++ "This utility takes a dataset and binarizes the " + "variables into either 0 or 1 given threshold. User can apply binarization " + "on a dimension or the whole dataset. The dimension to apply binarization " + "to can be specified using the " + PRINT_PARAM_STRING("dimension") + +@@ -38,7 +44,10 @@ PROGRAM_INFO("Binarize Data", "This utility takes a dataset and binarizes the " + PRINT_DATASET("X") + ", we could instead run" + "\n\n" + + PRINT_CALL("preprocess_binarize", "input", "X", "threshold", 5.0, +- "dimension", 0, "output", "Y")); ++ "dimension", 0, "output", "Y"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Input data matrix.", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +index 744f826a5..43b6df7f5 100644 +--- a/src/mlpack/methods/preprocess/preprocess_describe_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_describe_main.cpp +@@ -22,12 +22,17 @@ using namespace mlpack::util; + using namespace std; + using namespace boost; + +-PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " +- "prints out the descriptive statistics of the data. Descriptive statistics " +- "is the discipline of quantitatively describing the main features of a " +- "collection of information, or the quantitative description itself. The " +- "program does not modify the original file, but instead prints out the " +- "statistics to the console. The printed result will look like a table." ++PROGRAM_INFO("Descriptive Statistics", ++ // Short description. ++ "A utility for printing descriptive statistics about a dataset. This " ++ "prints a number of details about a dataset in a tabular format.", ++ // Long description. ++ "This utility takes a dataset and prints out the descriptive statistics " ++ "of the data. Descriptive statistics is the discipline of quantitatively " ++ "describing the main features of a collection of information, or the " ++ "quantitative description itself. The program does not modify the original " ++ "file, but instead prints out the statistics to the console. The printed " ++ "result will look like a table." + "\n\n" + "Optionally, width and precision of the output can be adjusted by a user " + "using the " + PRINT_PARAM_STRING("width") + " and " + +@@ -47,7 +52,10 @@ PROGRAM_INFO("Descriptive Statistics", "This utility takes a dataset and " + "the dataset as a population, we could run" + "\n\n" + + PRINT_CALL("preprocess_describe", "input", "X", "width", 10, "precision", 5, +- "verbose", true)); ++ "verbose", true), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data,", "i"); +diff --git a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +index 848bbe9cf..1825334ad 100644 +--- a/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_imputer_main.cpp +@@ -23,8 +23,14 @@ + #include + #include + +-PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " +- "defined missing variable to another to provide more meaningful analysis " ++PROGRAM_INFO("Impute Data", ++ // Short description. ++ "This utility provides several imputation strategies for missing data. " ++ "Given a dataset with missing values, this can impute according to several " ++ "strategies, including user-defined values.", ++ // Long description. ++ "This utility takes a dataset and converts a user-defined missing variable " ++ "to another to provide more meaningful analysis." + "\n\n" + "The program does not modify the original file, but instead makes a " + "separate file to save the output data; You can save the output by " +@@ -35,7 +41,10 @@ PROGRAM_INFO("Impute Data", "This utility takes a dataset and converts user " + "column-wise dataset, and save the result to result.csv, we could run" + "\n\n" + "$ mlpack_preprocess_imputer -i dataset.csv -o result.csv -m NULL -d 0 \n" +- "> -s listwise_deletion"); ++ "> -s listwise_deletion", ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_split", "#preprocess_split")); + + PARAM_STRING_IN_REQ("input_file", "File containing data.", "i"); + PARAM_STRING_OUT("output_file", "File to save output into.", "o"); +diff --git a/src/mlpack/methods/preprocess/preprocess_split_main.cpp b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +index e789e0250..4b510b790 100644 +--- a/src/mlpack/methods/preprocess/preprocess_split_main.cpp ++++ b/src/mlpack/methods/preprocess/preprocess_split_main.cpp +@@ -15,11 +15,16 @@ + #include + #include + +-PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " +- "and splits them into a training set and a test set. Before the split, the " +- "points in the dataset are randomly reordered. The percentage of the " +- "dataset to be used as the test set can be specified with the " + +- PRINT_PARAM_STRING("test_ratio") + " parameter; the default is 0.2 (20%)." ++PROGRAM_INFO("Split Data", ++ // Short description. ++ "A utility to split data into a training and testing dataset. This can " ++ "also split labels according to the same split.", ++ // Long description. ++ "This utility takes a dataset and optionally labels and splits them into a " ++ "training set and a test set. Before the split, the points in the dataset " ++ "are randomly reordered. The percentage of the dataset to be used as the " ++ "test set can be specified with the " + PRINT_PARAM_STRING("test_ratio") + ++ " parameter; the default is 0.2 (20%)." + "\n\n" + "The output training and test matrices may be saved with the " + + PRINT_PARAM_STRING("training") + " and " + PRINT_PARAM_STRING("test") + +@@ -48,7 +53,10 @@ PROGRAM_INFO("Split Data", "This utility takes a dataset and optionally labels " + "\n\n" + + PRINT_CALL("preprocess_split", "input", "X", "input_labels", "y", + "test_ratio", 0.3, "training", "X_train", "training_labels", "y_train", +- "test", "X_test", "test_labels", "y_test")); ++ "test", "X_test", "test_labels", "y_test"), ++ SEE_ALSO("@preprocess_binarize", "#preprocess_binarize"), ++ SEE_ALSO("@preprocess_describe", "#preprocess_describe"), ++ SEE_ALSO("@preprocess_imputer", "#preprocess_imputer")); + + // Define parameters for data. + PARAM_MATRIX_IN_REQ("input", "Matrix containing data.", "i"); +diff --git a/src/mlpack/methods/radical/CMakeLists.txt b/src/mlpack/methods/radical/CMakeLists.txt +index 886566b11..c7dc29cac 100644 +--- a/src/mlpack/methods/radical/CMakeLists.txt ++++ b/src/mlpack/methods/radical/CMakeLists.txt +@@ -15,3 +15,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(radical) + add_python_binding(radical) ++add_markdown_docs(radical "cli;python" "transformations") +diff --git a/src/mlpack/methods/radical/radical_main.cpp b/src/mlpack/methods/radical/radical_main.cpp +index 129b3d7ac..7a2a51e36 100644 +--- a/src/mlpack/methods/radical/radical_main.cpp ++++ b/src/mlpack/methods/radical/radical_main.cpp +@@ -15,11 +15,18 @@ + #include + #include "radical.hpp" + +-PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" +- "component analysis (ICA). Assuming that we have an input matrix X, the" +- "goal is to find a square unmixing matrix W such that Y = W * X and the " +- "dimensions of Y are independent components. If the algorithm is running" +- "particularly slowly, try reducing the number of replicates." ++PROGRAM_INFO("RADICAL", ++ // Short description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Given a dataset, this can decompose the dataset into an unmixing " ++ "matrix and an independent component matrix; this can be useful for " ++ "preprocessing.", ++ // Long description. ++ "An implementation of RADICAL, a method for independent component analysis " ++ "(ICA). Assuming that we have an input matrix X, the goal is to find a " ++ "square unmixing matrix W such that Y = W * X and the dimensions of Y are " ++ "independent components. If the algorithm is running particularly slowly, " ++ "try reducing the number of replicates." + "\n\n" + "The input matrix to perform ICA on should be specified with the " + + PRINT_PARAM_STRING("input") + " parameter. The output matrix Y may be " +@@ -31,7 +38,14 @@ PROGRAM_INFO("RADICAL", "An implementation of RADICAL, a method for independent" + "40 replicates, saving the independent components to " + + PRINT_DATASET("ic") + ", the following command may be used: " + "\n\n" + +- PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic")); ++ PRINT_CALL("radical", "input", "X", "replicates", 40, "output_ic", "ic"), ++ SEE_ALSO("Independent component analysis on Wikipedia", ++ "https://en.wikipedia.org/wiki/Independent_component_analysis"), ++ SEE_ALSO("ICA using spacings estimates of entropy (pdf)", ++ "http://www.jmlr.org/papers/volume4/learned-miller03a/" ++ "learned-miller03a.pdf"), ++ SEE_ALSO("mlpack::radical::Radical C++ class documentation", ++ "@doxygen/classmlpack_1_1radical_1_1Radical.html")); + + PARAM_MATRIX_IN_REQ("input", "Input dataset for ICA.", "i"); + +diff --git a/src/mlpack/methods/random_forest/CMakeLists.txt b/src/mlpack/methods/random_forest/CMakeLists.txt +index bfc6121ad..11872c4a1 100644 +--- a/src/mlpack/methods/random_forest/CMakeLists.txt ++++ b/src/mlpack/methods/random_forest/CMakeLists.txt +@@ -17,3 +17,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(random_forest) + add_python_binding(random_forest) ++add_markdown_docs(random_forest "cli;python" "classification") +diff --git a/src/mlpack/methods/random_forest/random_forest_main.cpp b/src/mlpack/methods/random_forest/random_forest_main.cpp +index 2ca69b853..4915f1cd5 100644 +--- a/src/mlpack/methods/random_forest/random_forest_main.cpp ++++ b/src/mlpack/methods/random_forest/random_forest_main.cpp +@@ -20,6 +20,12 @@ using namespace mlpack::util; + using namespace std; + + PROGRAM_INFO("Random forests", ++ // Short description. ++ "An implementation of the standard random forest algorithm by Leo Breiman " ++ "for classification. Given labeled data, a random forest can be trained " ++ "and saved for future use; or, a pre-trained random forest can be used for " ++ "classification.", ++ // Long description. + "This program is an implementation of the standard random forest " + "classification algorithm by Leo Breiman. A random forest can be " + "trained and saved for later use, or a random forest may be loaded " +@@ -70,7 +76,16 @@ PROGRAM_INFO("Random forests", + "could call " + "\n\n" + + PRINT_CALL("random_forest", "input_model", "rf_model", "test", "test_set", +- "test_labels", "test_labels", "predictions", "predictions")); ++ "test_labels", "test_labels", "predictions", "predictions"), ++ SEE_ALSO("@decision_tree", "#decision_tree"), ++ SEE_ALSO("@hoeffding_tree", "#hoeffding_tree"), ++ SEE_ALSO("@softmax_regression", "#softmax_regression"), ++ SEE_ALSO("Random forest on Wikipedia", ++ "https://en.wikipedia.org/wiki/Random_forest"), ++ SEE_ALSO("Random forests (pdf)", ++ "https://link.springer.com/content/pdf/10.1023/A:1010933404324.pdf"), ++ SEE_ALSO("mlpack::tree::RandomForest C++ class documentation", ++ "@doxygen/classmlpack_1_1tree_1_1RandomForest.html")); + + PARAM_MATRIX_IN("training", "Training dataset.", "t"); + PARAM_UROW_IN("labels", "Labels for training dataset.", "l"); +diff --git a/src/mlpack/methods/range_search/CMakeLists.txt b/src/mlpack/methods/range_search/CMakeLists.txt +index 9d29343db..68a020315 100644 +--- a/src/mlpack/methods/range_search/CMakeLists.txt ++++ b/src/mlpack/methods/range_search/CMakeLists.txt +@@ -21,3 +21,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(range_search) + #add_python_binding(range_search) ++add_markdown_docs(range_search "cli" "geometry") +diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp +index 27e49caf1..c50bb709b 100644 +--- a/src/mlpack/methods/range_search/range_search_main.cpp ++++ b/src/mlpack/methods/range_search/range_search_main.cpp +@@ -29,6 +29,13 @@ using namespace mlpack::util; + + // Information about the program itself. + PROGRAM_INFO("Range Search", ++ // Short description. ++ "An implementation of range search with single-tree and dual-tree " ++ "algorithms. Given a set of reference points and a set of query points and" ++ " a range, this can find the set of reference points within the desired " ++ "range for each query point, and any trees built during the computation can" ++ " be saved for reuse with future range searches.", ++ // Long description. + "This program implements range search with a Euclidean distance metric. " + "For a given query point, a given range, and a given set of reference " + "points, the program will return all of the reference points with distance " +@@ -55,7 +62,14 @@ PROGRAM_INFO("Range Search", + " resultant CSV-like files may not be loadable by many programs. However, " + "at this time a better way to store this non-square result is not known. " + "As a result, any output files will be written as CSVs in this manner, " +- "regardless of the given extension."); ++ "regardless of the given extension.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("Range searching on Wikipedia", ++ "https://en.wikipedia.org/wiki/Range_searching"), ++ SEE_ALSO("Tree-independent dual-tree algorithms (pdf)", ++ "http://proceedings.mlr.press/v28/curtin13.pdf"), ++ SEE_ALSO("mlpack::range::RangeSearch C++ class documentation", ++ "@doxygen/classmlpack_1_1range_1_1RangeSearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/rann/CMakeLists.txt b/src/mlpack/methods/rann/CMakeLists.txt +index 457a11dd6..3ba78994a 100644 +--- a/src/mlpack/methods/rann/CMakeLists.txt ++++ b/src/mlpack/methods/rann/CMakeLists.txt +@@ -37,3 +37,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + # reference sets. + add_cli_executable(krann) + add_python_binding(krann) ++add_markdown_docs(krann "cli;python" "geometry") +diff --git a/src/mlpack/methods/rann/krann_main.cpp b/src/mlpack/methods/rann/krann_main.cpp +index 8224a2113..94661a53f 100644 +--- a/src/mlpack/methods/rann/krann_main.cpp ++++ b/src/mlpack/methods/rann/krann_main.cpp +@@ -30,6 +30,13 @@ typedef RAModel RANNModel; + + // Information about the program itself. + PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", ++ // Short description. ++ "An implementation of rank-approximate k-nearest-neighbor search (kRANN) " ++ " using single-tree and dual-tree algorithms. Given a set of reference " ++ "points and query points, this can find the k nearest neighbors in the " ++ "reference set of each query point using trees; trees that are built can " ++ "be saved for future use.", ++ // Long description. + "This program will calculate the k rank-approximate-nearest-neighbors of a " + "set of points. You may specify a separate set of reference points and " + "query points, or just a reference set which will be used as both the " +@@ -55,7 +62,15 @@ PROGRAM_INFO("K-Rank-Approximate-Nearest-Neighbors (kRANN)", + "neighbors output file corresponds to the index of the point in the " + "reference set which is the i'th nearest neighbor from the point in the " + "query set with index j. Row i and column j in the distances output file " +- "corresponds to the distance between those two points."); ++ "corresponds to the distance between those two points.", ++ SEE_ALSO("@knn", "#knn"), ++ SEE_ALSO("@lsh", "#lsh"), ++ SEE_ALSO("Rank-approximate nearest neighbor search: Retaining meaning and " ++ "speed in high dimensions (pdf)", "https://papers.nips.cc/paper/3864-" ++ "rank-approximate-nearest-neighbor-search-retaining-meaning-and-speed-" ++ "in-high-dimensions.pdf"), ++ SEE_ALSO("mlpack::neighbor::RASearch C++ class documentation", ++ "@doxygen/classmlpack_1_1neighbor_1_1RASearch.html")); + + // Define our input parameters that this program will take. + PARAM_MATRIX_IN("reference", "Matrix containing the reference dataset.", "r"); +diff --git a/src/mlpack/methods/softmax_regression/CMakeLists.txt b/src/mlpack/methods/softmax_regression/CMakeLists.txt +index c6770f7c4..60a7d4cff 100644 +--- a/src/mlpack/methods/softmax_regression/CMakeLists.txt ++++ b/src/mlpack/methods/softmax_regression/CMakeLists.txt +@@ -19,3 +19,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(softmax_regression) + add_python_binding(softmax_regression) ++add_markdown_docs(softmax_regression "cli;python" "classification") +diff --git a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +index 97b54a2a5..4e3f88e76 100644 +--- a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp ++++ b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +@@ -24,11 +24,18 @@ using namespace mlpack::regression; + using namespace mlpack::util; + + // Define parameters for the executable. +-PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " +- "a generalization of logistic regression to the multiclass case, and has " +- "support for L2 regularization. The program is able to train a model, load" +- " an existing model, and give predictions (and optionally their accuracy) " +- "for test data." ++PROGRAM_INFO("Softmax Regression", ++ // Short description. ++ "An implementation of softmax regression for classification, which is a " ++ "multiclass generalization of logistic regression. Given labeled data, a " ++ "softmax regression model can be trained and saved for future use, or, a " ++ "pre-trained softmax regression model can be used for classification of " ++ "new points.", ++ // Long description. ++ "This program performs softmax regression, a generalization of logistic " ++ "regression to the multiclass case, and has support for L2 regularization. " ++ " The program is able to train a model, load an existing model, and give " ++ "predictions (and optionally their accuracy) for test data." + "\n\n" + "Training a softmax regression model is done by giving a file of training " + "points with the " + PRINT_PARAM_STRING("training") + " parameter and their" +@@ -71,7 +78,14 @@ PROGRAM_INFO("Softmax Regression", "This program performs softmax regression, " + " " + PRINT_DATASET("predictions") + ", the following command can be used:" + "\n\n" + + PRINT_CALL("softmax_regression", "input_model", "sr_model", "test", +- "test_points", "predictions", "predictions")); ++ "test_points", "predictions", "predictions"), ++ SEE_ALSO("@logistic_regression", "#logistic_regression"), ++ SEE_ALSO("@random_forest", "#random_forest"), ++ SEE_ALSO("Multinomial logistic regression (softmax regression) on " ++ "Wikipedia", ++ "https://en.wikipedia.org/wiki/Multinomial_logistic_regression"), ++ SEE_ALSO("mlpack::regression::SoftmaxRegression C++ class documentation", ++ "@doxygen/classmlpack_1_1regression_1_1SoftmaxRegression.html")); + + // Required options. + PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix " +diff --git a/src/mlpack/methods/sparse_coding/CMakeLists.txt b/src/mlpack/methods/sparse_coding/CMakeLists.txt +index cfd1108cb..7bb0f7b79 100644 +--- a/src/mlpack/methods/sparse_coding/CMakeLists.txt ++++ b/src/mlpack/methods/sparse_coding/CMakeLists.txt +@@ -20,3 +20,4 @@ set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + + add_cli_executable(sparse_coding) + add_python_binding(sparse_coding) ++add_markdown_docs(sparse_coding "cli;python" "transformations") +diff --git a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +index e4b561f53..515cb78eb 100644 +--- a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp ++++ b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +@@ -22,12 +22,20 @@ using namespace mlpack::math; + using namespace mlpack::sparse_coding; + using namespace mlpack::util; + +-PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " +- "Dictionary Learning, which achieves sparsity via an l1-norm regularizer on" +- " the codes (LASSO) or an (l1+l2)-norm regularizer on the codes (the " +- "Elastic Net). Given a dense data matrix X with d dimensions and n points," +- " sparse coding seeks to find a dense dictionary matrix D with k atoms in " +- "d dimensions, and a sparse coding matrix Z with n points in k dimensions." ++PROGRAM_INFO("Sparse Coding", ++ // Short description. ++ "An implementation of Sparse Coding with Dictionary Learning. Given a " ++ "dataset, this will decompose the dataset into a sparse combination of a " ++ "few dictionary elements, where the dictionary is learned during " ++ "computation; a dictionary can be reused for future sparse coding of new " ++ "points.", ++ // Long description. ++ "An implementation of Sparse Coding with Dictionary Learning, which " ++ "achieves sparsity via an l1-norm regularizer on the codes (LASSO) or an " ++ "(l1+l2)-norm regularizer on the codes (the Elastic Net). Given a dense " ++ "data matrix X with d dimensions and n points, sparse coding seeks to find " ++ "a dense dictionary matrix D with k atoms in d dimensions, and a sparse " ++ "coding matrix Z with n points in k dimensions." + "\n\n" + "The original data matrix X can then be reconstructed as Z * D. Therefore," + " this program finds a representation of each point in X as a sparse linear" +@@ -62,7 +70,18 @@ PROGRAM_INFO("Sparse Coding", "An implementation of Sparse Coding with " + PRINT_DATASET("codes") + ": " + "\n\n" + + PRINT_CALL("sparse_coding", "input_model", "model", "test", "otherdata", +- "codes", "codes")); ++ "codes", "codes"), ++ SEE_ALSO("@local_coordinate_coding", "#local_coordinate_coding"), ++ SEE_ALSO("Sparse dictionary learning on Wikipedia", ++ "https://en.wikipedia.org/wiki/Sparse_dictionary_learning"), ++ SEE_ALSO("Efficient sparse coding algorithms (pdf)", ++ "http://papers.nips.cc/paper/2979-efficient-sparse-coding-" ++ "algorithms.pdf"), ++ SEE_ALSO("Regularization and variable selection via the elastic net", ++ "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4696&" ++ "rep=rep1&type=pdf"), ++ SEE_ALSO("mlpack::sparse_coding::SparseCoding C++ class documentation", ++ "@doxygen/classmlpack_1_1sparse__coding_1_1SparseCoding.html")); + + // Train the model. + PARAM_MATRIX_IN("training", "Matrix of training data (X).", "t"); +-- +2.20.1 + diff --git a/_src/markdown-patches/mlpack-git-markdown.patch b/_src/markdown-patches/mlpack-git-markdown.patch new file mode 100644 index 000000000..c044b657c --- /dev/null +++ b/_src/markdown-patches/mlpack-git-markdown.patch @@ -0,0 +1,26 @@ +From 711a3a1f6d87695e40fdf4b52c4edfc11b603e0e Mon Sep 17 00:00:00 2001 +From: Ryan Curtin +Date: Wed, 6 Mar 2019 21:15:21 -0500 +Subject: [PATCH] Fix minor bug. + +--- + src/mlpack/core/util/mlpack_main.hpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/mlpack/core/util/mlpack_main.hpp b/src/mlpack/core/util/mlpack_main.hpp +index 1c39c33e2..3899d49b9 100644 +--- a/src/mlpack/core/util/mlpack_main.hpp ++++ b/src/mlpack/core/util/mlpack_main.hpp +@@ -162,6 +162,9 @@ PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" + + #elif BINDING_TYPE == BINDING_TYPE_MARKDOWN + ++// It doesn't really matter whether this is true or false... ++#define BINDING_MATRIX_TRANSPOSED true ++ + // We use BINDING_NAME in PROGRAM_INFO() so it needs to be defined. + #ifndef BINDING_NAME + #error "BINDING_NAME must be defined when BINDING_TYPE is Markdown!" +-- +2.20.1 + diff --git a/_src/mlpack-3.0.0.tar.gz b/_src/mlpack-3.0.0.tar.gz new file mode 100644 index 000000000..6e69a049f Binary files /dev/null and b/_src/mlpack-3.0.0.tar.gz differ diff --git a/_src/mlpack-3.0.1.tar.gz b/_src/mlpack-3.0.1.tar.gz new file mode 100644 index 000000000..8405891e9 Binary files /dev/null and b/_src/mlpack-3.0.1.tar.gz differ diff --git a/_src/mlpack-3.0.2.tar.gz b/_src/mlpack-3.0.2.tar.gz new file mode 100644 index 000000000..7bbbe99be Binary files /dev/null and b/_src/mlpack-3.0.2.tar.gz differ diff --git a/_src/mlpack-3.0.3.tar.gz b/_src/mlpack-3.0.3.tar.gz new file mode 100644 index 000000000..89126a025 Binary files /dev/null and b/_src/mlpack-3.0.3.tar.gz differ diff --git a/_src/mlpack-3.0.4.tar.gz b/_src/mlpack-3.0.4.tar.gz new file mode 100644 index 000000000..b6b116c6c Binary files /dev/null and b/_src/mlpack-3.0.4.tar.gz differ diff --git a/_src/mlpack-git.tar.gz b/_src/mlpack-git.tar.gz new file mode 100644 index 000000000..e69de29bb diff --git a/_src/scripts/build-version.sh b/_src/scripts/build-version.sh new file mode 100755 index 000000000..60e9ed10a --- /dev/null +++ b/_src/scripts/build-version.sh @@ -0,0 +1,170 @@ +#!/bin/bash +# +# Build the source that we will extract and use for a given version. When this +# is complete, the directory doc/mlpack-${version}/ will be populated with +# binding_header.md and binding_documentation.md and changelog.md, and the +# doxygen/ directory. +# +# For mlpack-git, this is the only script that needs to be run nightly. +# +# This also expects src/mlpack-$1.tar.gz to exist. +# +# $1: version (i.e. "3.0.4"). + +version=$1; + +srcdir="$PWD/_src/mlpack-$1" +docdir="$PWD/doc/mlpack-$1" +doxygensrcdir="$PWD/_src/doxygen" + +# Get list of versions. +doxygen_versions=(); +for i in `ls _src/mlpack-*.tar.gz | sort -r`; +do + doxygen_versions+=("`basename $i .tar.gz | sed 's/mlpack-//'`"); +done + +# Unpack and patch the code if needed. Remove any old code. +echo "Unpacking and patching version $version..."; +cd _src/; +rm -rf mlpack-$version/; + +# Handle git repository separately. Note that mlpack-git.tar.gz is just a +# placeholder file. +if [ "a$version" == "agit" ]; +then + git clone https://github.com/mlpack/mlpack mlpack-git/; +else + tar -xvzpf mlpack-$version.tar.gz; +fi + +if [ -f markdown-patches/mlpack-$version-markdown.patch ]; +then + echo "Patch mlpack-$version-markdown.patch found; applying..."; + cd mlpack-$version/; + patch -p1 < ../markdown-patches/mlpack-$version-markdown.patch; + cp ../markdown-patches/menu_bg.png _src/mlpack/bindings/markdown/res/; + cd ../; +fi +cd ../; + +# Remove any old directory if needed. +echo "Creating output directory for Markdown output..."; +rm -rf doc/mlpack-$version/; +mkdir -p doc/mlpack-$version/; + +echo "Generating documentation for version $version..."; + +# Build the Markdown documentation. This assumes that each version has the +# Markdown bindings available as a target. +cd _src/mlpack-$version/; +mkdir -p build/; +cd build/; +cmake -DBUILD_MARKDOWN_BINDINGS=ON ../; +make -j4 markdown; +if [ "$?" -ne "0" ]; +then + echo "Failure building Markdown bindings for version ${version}!"; + exit 1; +fi + +# Copy the Markdown documentation to the right place. +cat doc/mlpack.md |\ + awk '/