summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Doxyfile.in4
-rw-r--r--src/common/doc/footer.html13
-rw-r--r--src/common/doc/header.html12
-rwxr-xr-x[-rw-r--r--]src/common/doc/stylesheet.css1371
-rw-r--r--src/python/CMakeLists.txt9
-rw-r--r--src/python/doc/cubical_complex_sum.inc25
-rw-r--r--src/python/doc/cubical_complex_tflow_itf_ref.rst40
-rw-r--r--src/python/doc/differentiation_sum.inc12
-rw-r--r--src/python/doc/installation.rst6
-rw-r--r--src/python/doc/ls_simplex_tree_tflow_itf_ref.rst53
-rw-r--r--src/python/doc/rips_complex_sum.inc5
-rw-r--r--src/python/doc/rips_complex_tflow_itf_ref.rst48
-rw-r--r--src/python/doc/simplex_tree_sum.inc23
-rw-r--r--src/python/gudhi/tensorflow/__init__.py5
-rw-r--r--src/python/gudhi/tensorflow/cubical_layer.py82
-rw-r--r--src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py87
-rw-r--r--src/python/gudhi/tensorflow/rips_layer.py93
-rw-r--r--src/python/test/test_diff.py78
18 files changed, 575 insertions, 1391 deletions
diff --git a/src/Doxyfile.in b/src/Doxyfile.in
index 06a74012..d0fad45f 100644
--- a/src/Doxyfile.in
+++ b/src/Doxyfile.in
@@ -1117,7 +1117,7 @@ HTML_FOOTER = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/footer.html
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_STYLESHEET = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/stylesheet.css
+HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
@@ -1130,7 +1130,7 @@ HTML_STYLESHEET = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/stylesheet.css
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_EXTRA_STYLESHEET =
+HTML_EXTRA_STYLESHEET = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/stylesheet.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
diff --git a/src/common/doc/footer.html b/src/common/doc/footer.html
index 4168c6bc..08a2cbd0 100644
--- a/src/common/doc/footer.html
+++ b/src/common/doc/footer.html
@@ -1,5 +1,9 @@
-<!-- HTML footer for doxygen 1.8.6-->
+<!-- HTML footer for doxygen 1.9.4-->
<!-- start footer part -->
+<!--BEGIN GENERATE_TREEVIEW-->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+<!--END GENERATE_TREEVIEW-->
+<ul>
<table style="width:100%">
<tr class="no-bullet shadow-black">
<td class="network-entypo">
@@ -10,14 +14,15 @@
<!--END PROJECT_NAME-->
</td>
<td class="network-entypo">
-<!--BEGIN GENERATE_TREEVIEW-->
$generatedby
<a href="http://www.doxygen.org/index.html">
Doxygen</a> $doxygenversion
-<!--END GENERATE_TREEVIEW-->
</td>
</tr>
</table>
-
+</ul>
+<!--BEGIN GENERATE_TREEVIEW-->
+</div>
+<!--END GENERATE_TREEVIEW-->
</body>
</html>
diff --git a/src/common/doc/header.html b/src/common/doc/header.html
index 7c20478b..a97e1b2f 100644
--- a/src/common/doc/header.html
+++ b/src/common/doc/header.html
@@ -8,9 +8,6 @@
<meta name="generator" content="Doxygen $doxygenversion"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
-<!-- GUDHI website css for header BEGIN -->
-<link rel="stylesheet" type="text/css" href="https://gudhi.inria.fr/assets/css/styles_feeling_responsive.css" />
-<!-- GUDHI website css for header END -->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
@@ -18,13 +15,17 @@ $treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
+<!-- GUDHI website css for header BEGIN -->
+<link rel="stylesheet" type="text/css" href="https://gudhi.inria.fr/assets/css/styles_feeling_responsive.css" />
+<!-- GUDHI website css for header END -->
$extrastylesheet
</head>
<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!-- GUDHI website header BEGIN -->
<div id="navigation" class="sticky">
- <nav class="top-bar" role="navigation" data-topbar>
+ <nav class="top-bar" role="navigation" data-topbar="true">
<ul class="title-area">
<li class="name">
<h1 class="show-for-small-only"><a href="" class="icon-tree"> GUDHI library</a></h1>
@@ -38,7 +39,7 @@ $extrastylesheet
<li><a href="/contact/">Contact</a></li>
</ul>
<ul class="left">
- <li><a href="/"> <img src="/assets/img/home.png" alt=" GUDHI"> GUDHI </a></li>
+ <li><a href="/"> <img src="/assets/img/home.png" alt=" GUDHI"/> GUDHI </a></li>
<li class="divider"></li>
<li class="has-dropdown">
<a href="#">Project</a>
@@ -85,7 +86,6 @@ $extrastylesheet
</div><!-- /#navigation -->
<!-- GUDHI website header END -->
-<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
diff --git a/src/common/doc/stylesheet.css b/src/common/doc/stylesheet.css
index 1df177a4..fb030e1f 100644..100755
--- a/src/common/doc/stylesheet.css
+++ b/src/common/doc/stylesheet.css
@@ -1,1367 +1,28 @@
-/* The standard CSS for doxygen 1.8.6 */
-
-body, table, div, p, dl {
- font: 400 14px/22px Roboto,sans-serif;
-}
-
-/* @group Heading Levels */
-
-h1.groupheader {
- font-size: 150%;
-}
-
-.title {
- font: 400 14px/28px Roboto,sans-serif;
- font-size: 150%;
- font-weight: bold;
- margin: 10px 2px;
-}
-
-h2.groupheader {
- border-bottom: 1px solid #879ECB;
- color: #354C7B;
- font-size: 150%;
- font-weight: normal;
- margin-top: 1.75em;
- padding-top: 8px;
- padding-bottom: 4px;
- width: 100%;
-}
-
-h3.groupheader {
- font-size: 100%;
-}
-
-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;
- margin-right: 15px;
-}
-
-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: #EBEFF6;
- border: 1px solid #A3B4D7;
- text-align: center;
-}
-
-div.qindex, div.navpath {
- width: 100%;
- line-height: 140%;
-}
-
-div.navtab {
- margin-right: 15px;
-}
-
-/* @group Link Styling */
-
-a {
- color: #3D578C;
- font-weight: normal;
- text-decoration: none;
-}
-
-.contents a:visited {
- color: #4665A2;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-a.qindex {
- font-weight: bold;
-}
-
-a.qindexHL {
- font-weight: bold;
- background-color: #9CAFD4;
- color: #ffffff;
- border: 1px double #869DCA;
-}
-
-.contents a.qindexHL:visited {
- color: #ffffff;
-}
-
-a.el {
- font-weight: bold;
-}
-
-a.elRef {
-}
-
-a.code, a.code:visited, a.line, a.line:visited {
- color: #4665A2;
-}
-
-a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
- color: #4665A2;
-}
-
-/* @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 {
- padding: 4px 6px;
- margin: 4px 8px 4px 2px;
- background-color: #FBFCFD;
- border: 1px solid #C4CFE5;
-}
-
-div.line {
- font-family: monospace, fixed;
- font-size: 13px;
- min-height: 13px;
- 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.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: #E8E8E8;
- white-space: pre;
-}
-span.lineno a {
- background-color: #D8D8D8;
-}
-
-span.lineno a:hover {
- background-color: #C8C8C8;
-}
-
-div.ah {
- background-color: black;
- font-weight: bold;
- color: #ffffff;
- 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);
-}
-
-div.groupHeader {
- margin-left: 16px;
- margin-top: 12px;
- font-weight: bold;
-}
-
-div.groupText {
- margin-left: 16px;
- font-style: italic;
-}
-
-body {
- background-color: white;
- color: black;
- margin: 0;
-}
-
-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.formulaDsp {
-
-}
-
-img.formulaInl {
- vertical-align: middle;
-}
-
-div.center {
- text-align: center;
- margin-top: 0px;
- margin-bottom: 0px;
- padding: 0px;
-}
-
-div.center img {
- border: 0px;
-}
-
-address.footer {
- text-align: right;
- padding-right: 12px;
-}
-
-img.footer {
- border: 0px;
- vertical-align: middle;
-}
-
-/* @group Code Colorization */
-
-span.keyword {
- color: #008000
-}
-
-span.keywordtype {
- color: #604020
-}
-
-span.keywordflow {
- color: #e08000
-}
-
-span.comment {
- color: #800000
-}
-
-span.preprocessor {
- color: #806020
-}
-
-span.stringliteral {
- color: #002080
-}
-
-span.charliteral {
- color: #008080
-}
-
-span.vhdldigit {
- color: #ff00ff
-}
-
-span.vhdlchar {
- color: #000000
-}
-
-span.vhdlkeyword {
- color: #700070
-}
-
-span.vhdllogic {
- color: #ff0000
-}
-
-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: 1px solid #4A6AAA;
-}
-
-hr.footer {
- height: 1px;
-}
-
-/* @group Member Descriptions */
-
-table.memberdecls {
- border-spacing: 0px;
- padding: 0px;
-}
-
-.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;
-}
-
-.memberdecls td.glow, .fieldtable tr.glow {
- background-color: cyan;
- box-shadow: 0 0 15px cyan;
-}
-
-.mdescLeft, .mdescRight,
-.memItemLeft, .memItemRight,
-.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
- background-color: #F9FAFC;
- border: none;
- margin: 4px;
- padding: 1px 0 0 8px;
-}
-
-.mdescLeft, .mdescRight {
- padding: 0px 8px 4px 8px;
- color: #555;
-}
-
-.memSeparator {
- border-bottom: 1px solid #DEE4F0;
- line-height: 1px;
- margin: 0px;
- padding: 0px;
-}
-
-.memItemLeft, .memTemplItemLeft {
- white-space: nowrap;
-}
-
-.memItemRight {
- width: 100%;
-}
-
-.memTemplParams {
- color: #4665A2;
- white-space: nowrap;
- font-size: 80%;
-}
-
-/* @end */
-
-/* @group Member Details */
-
-/* Styles for detailed member documentation */
-
-.memtemplate {
- font-size: 80%;
- color: #4665A2;
- font-weight: normal;
- margin-left: 9px;
-}
-
-.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: 100%;
-}
-
-.memitem.glow {
- box-shadow: 0 0 15px cyan;
-}
-
-.memname {
- font-weight: bold;
- margin-left: 6px;
-}
-
-.memname td {
- vertical-align: bottom;
-}
-
-.memproto, dl.reflist dt {
- border-top: 1px solid #A8B8D9;
- border-left: 1px solid #A8B8D9;
- border-right: 1px solid #A8B8D9;
- padding: 6px 0px 6px 0px;
- color: #253555;
- font-weight: bold;
- text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
- background-image:url('nav_f.png');
- background-repeat:repeat-x;
- background-color: #E2E8F2;
- /* opera specific markup */
- box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
- border-top-right-radius: 4px;
- border-top-left-radius: 4px;
- /* firefox specific markup */
- -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-topleft: 4px;
- /* webkit specific markup */
- -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
- -webkit-border-top-right-radius: 4px;
- -webkit-border-top-left-radius: 4px;
-
-}
-
-.memdoc, dl.reflist dd {
- border-bottom: 1px solid #A8B8D9;
- border-left: 1px solid #A8B8D9;
- border-right: 1px solid #A8B8D9;
- padding: 6px 10px 2px 10px;
- background-color: #FBFCFD;
- border-top-width: 0;
- background-image:url('nav_g.png');
- background-repeat:repeat-x;
- background-color: #FFFFFF;
- /* opera specific markup */
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 4px;
- box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
- /* 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: #602020;
- white-space: nowrap;
-}
-.paramname em {
- font-style: normal;
-}
-.paramname code {
- line-height: 14px;
-}
-
-.params, .retval, .exception, .tparams {
- margin-left: 0px;
- padding-left: 0px;
-}
-
-.params .paramname, .retval .paramname {
- font-weight: bold;
- vertical-align: top;
-}
-
-.params .paramtype {
- font-style: italic;
- vertical-align: top;
-}
-
-.params .paramdir {
- font-family: "courier new",courier,monospace;
- vertical-align: top;
-}
-
-table.mlabels {
- border-spacing: 0px;
-}
-
-td.mlabels-left {
- width: 100%;
- padding: 0px;
-}
-
-td.mlabels-right {
- vertical-align: bottom;
- padding: 0px;
- white-space: nowrap;
-}
-
-span.mlabels {
- margin-left: 8px;
-}
-
-span.mlabel {
- background-color: #728DC1;
- border-top:1px solid #5373B4;
- border-left:1px solid #5373B4;
- border-right:1px solid #C4CFE5;
- border-bottom:1px solid #C4CFE5;
- text-shadow: none;
- color: white;
- margin-right: 4px;
- padding: 2px 3px;
- border-radius: 3px;
- font-size: 7pt;
- white-space: nowrap;
- vertical-align: middle;
-}
-
-
-
-/* @end */
-
-/* these are for tree view when not used as main index */
-
-div.directory {
- margin: 10px 0px;
- border-top: 1px solid #A8B8D9;
- border-bottom: 1px solid #A8B8D9;
- width: 100%;
-}
-
-.directory table {
- border-collapse:collapse;
-}
-
-.directory td {
- 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: #F7F8FB;
-}
-
-.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: #3D578C;
-}
-
-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: #2A3D61;
-}
-
-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 #A8B8D9;
- 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);
-}
-
-.fieldtable td, .fieldtable th {
- padding: 3px 7px 2px;
-}
-
-.fieldtable td.fieldtype, .fieldtable td.fieldname {
- white-space: nowrap;
- border-right: 1px solid #A8B8D9;
- border-bottom: 1px solid #A8B8D9;
- vertical-align: top;
-}
-
-.fieldtable td.fieldname {
- padding-top: 3px;
-}
-
-.fieldtable td.fielddoc {
- border-bottom: 1px solid #A8B8D9;
- /*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-image:url('nav_f.png');
- background-repeat:repeat-x;
- background-color: #E2E8F2;
- font-size: 90%;
- color: #253555;
- padding-bottom: 4px;
- padding-top: 5px;
- text-align:left;
- -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 #A8B8D9;
-}
-
-
-.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-image:url('tab_b.png');
- background-repeat:repeat-x;
- background-position: 0 -5px;
- height:30px;
- line-height:30px;
- color:#8AA0CC;
- border:solid 1px #C2CDE4;
- overflow:hidden;
- margin:0px;
- padding:0px;
-}
-
-.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: #283A5D;
- font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;
- text-shadow: 0px 1px 1px rgba(255, 255, 255, 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;
-}
-
-div.ingroups
-{
- font-size: 8pt;
- width: 50%;
- text-align: left;
-}
-
-div.ingroups a
-{
- white-space: nowrap;
-}
-
-div.header
-{
- background-image:url('nav_h.png');
- background-repeat:repeat-x;
- background-color: #F9FAFC;
- margin: 0px;
- border-bottom: 1px solid #C4CFE5;
-}
-
-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:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #D0C000;
-}
-
-dl.warning, dl.attention
-{
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #FF0000;
-}
-
-dl.pre, dl.post, dl.invariant
-{
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #00D000;
-}
-
-dl.deprecated
-{
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #505050;
-}
-
-dl.todo
-{
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #00C0E0;
-}
-
-dl.test
-{
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #3030E0;
-}
-
-dl.bug
-{
- margin-left:-7px;
- 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;
-}
-
#projectname
{
- border: 0px none;
- font: 300% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 2px 0px;
+ border: 0px none;
}
-
#projectbrief
{
- font: 60% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 0px;
+ font: 60% Tahoma, Arial,sans-serif;
}
-
#projectnumber
{
- font: 80% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 0px;
-}
-
-#titlearea
-{
- padding: 0px;
- margin: 0px;
- width: 100%;
- border-bottom: 1px solid #5373B4;
-}
-
-.image
-{
- text-align: center;
-}
-
-.dotgraph
-{
- text-align: center;
+ font: 80% Tahoma, Arial,sans-serif;
}
-
-.mscgraph
+.arrow
{
- 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 20px 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;
+ width: auto;
+ height: auto;
+ padding-left: 16px;
}
-
-#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 Roboto,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: #ffffff;
- 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;
- }
+// With the doxygen versions <= 1.9.2 the default setting 'overflow: hidden;' causes problems.
+// With the commit:
+// Commit: 590198b416cd53313d150428d2f912586065ea0d [590198b]
+// Date: Wednesday, December 1, 2021 1:37:26 PM
+// issue #8924 Horizontal scroll bar missing in HTML for wide class="dotgraph" objects
+// for the doxygen 1.9.3 version this has already been corrected but to run properly with the <= 1.9.2 version
+// this setting is required
+ul {
+ overflow: visible;
}
-
diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt
index c3768475..65adef75 100644
--- a/src/python/CMakeLists.txt
+++ b/src/python/CMakeLists.txt
@@ -70,6 +70,7 @@ if(PYTHONINTERP_FOUND)
set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ")
# Modules that should not be auto-imported in __init__.py
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'representations', ")
+ set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'tensorflow', ")
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ")
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'point_cloud', ")
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'weighted_rips_complex', ")
@@ -289,7 +290,8 @@ if(PYTHONINTERP_FOUND)
# Other .py files
file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
file(COPY "gudhi/representations" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/")
- file(COPY "gudhi/wasserstein" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
+ file(COPY "gudhi/wasserstein" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
+ file(COPY "gudhi/tensorflow" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
file(COPY "gudhi/point_cloud" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
file(COPY "gudhi/clustering" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi" FILES_MATCHING PATTERN "*.py")
file(COPY "gudhi/weighted_rips_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
@@ -562,6 +564,11 @@ if(PYTHONINTERP_FOUND)
add_gudhi_py_test(test_representations)
endif()
+ # Differentiation
+ if(TENSORFLOW_FOUND)
+ add_gudhi_py_test(test_diff)
+ endif()
+
# Betti curves
if(SKLEARN_FOUND AND SCIPY_FOUND)
add_gudhi_py_test(test_betti_curve_representations)
diff --git a/src/python/doc/cubical_complex_sum.inc b/src/python/doc/cubical_complex_sum.inc
index 87db184d..90ec9fc2 100644
--- a/src/python/doc/cubical_complex_sum.inc
+++ b/src/python/doc/cubical_complex_sum.inc
@@ -1,14 +1,17 @@
.. table::
:widths: 30 40 30
- +--------------------------------------------------------------------------+----------------------------------------------------------------------+-----------------------------+
- | .. figure:: | The cubical complex represents a grid as a cell complex with | :Author: Pawel Dlotko |
- | ../../doc/Bitmap_cubical_complex/Cubical_complex_representation.png | cells of all dimensions. | |
- | :alt: Cubical complex representation | | :Since: GUDHI 2.0.0 |
- | :figclass: align-center | | |
- | | | :License: MIT |
- | | | |
- +--------------------------------------------------------------------------+----------------------------------------------------------------------+-----------------------------+
- | * :doc:`cubical_complex_user` | * :doc:`cubical_complex_ref` |
- | | * :doc:`periodic_cubical_complex_ref` |
- +--------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+
+ +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+
+ | .. figure:: | The cubical complex represents a grid as a cell complex with | :Author: Pawel Dlotko |
+ | ../../doc/Bitmap_cubical_complex/Cubical_complex_representation.png | cells of all dimensions. | |
+ | :alt: Cubical complex representation | | :Since: GUDHI 2.0.0 |
+ | :figclass: align-center | | |
+ | | | :License: MIT |
+ | | | |
+ +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+
+ | * :doc:`cubical_complex_user` | * :doc:`cubical_complex_ref` |
+ | | * :doc:`periodic_cubical_complex_ref` |
+ +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+
+ | | * :doc:`cubical_complex_tflow_itf_ref` | :requires: `TensorFlow <installation.html#tensorflow>`_ |
+ | | | |
+ +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+
diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst
new file mode 100644
index 00000000..b32f5e47
--- /dev/null
+++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst
@@ -0,0 +1,40 @@
+:orphan:
+
+.. To get rid of WARNING: document isn't included in any toctree
+
+TensorFlow layer for cubical persistence
+########################################
+
+.. include:: differentiation_sum.inc
+
+Example of gradient computed from cubical persistence
+-----------------------------------------------------
+
+.. testcode::
+
+ from gudhi.tensorflow import CubicalLayer
+ import tensorflow as tf
+
+ X = tf.Variable([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=tf.float32, trainable=True)
+ cl = CubicalLayer(homology_dimensions=[0])
+
+ with tf.GradientTape() as tape:
+ dgm = cl.call(X)[0][0]
+ loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0])))
+
+ grads = tape.gradient(loss, [X])
+ print(grads[0].numpy())
+
+.. testoutput::
+
+ [[ 0. 0. 0. ]
+ [ 0. 0.5 0. ]
+ [ 0. 0. -0.5]]
+
+Documentation for CubicalLayer
+------------------------------
+
+.. autoclass:: gudhi.tensorflow.CubicalLayer
+ :members:
+ :special-members: __init__
+ :show-inheritance:
diff --git a/src/python/doc/differentiation_sum.inc b/src/python/doc/differentiation_sum.inc
new file mode 100644
index 00000000..3aec33df
--- /dev/null
+++ b/src/python/doc/differentiation_sum.inc
@@ -0,0 +1,12 @@
+.. list-table::
+ :widths: 40 30 30
+ :header-rows: 0
+
+ * - :Since: GUDHI 3.5.0
+ - :License: MIT
+ - :Requires: `TensorFlow <installation.html#tensorflow>`_
+
+We provide TensorFlow 2 models that can handle automatic differentiation for the computation of persistence diagrams from complexes available in the Gudhi library.
+This includes simplex trees, cubical complexes and Vietoris-Rips complexes. Detailed example on how to use these layers in practice are available
+in the following `notebook <https://github.com/GUDHI/TDA-tutorial/blob/master/Tuto-GUDHI-optimization.ipynb>`_. Note that even if TensorFlow GPU is enabled, all
+internal computations using Gudhi will be done on CPU.
diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst
index cff84691..dd476054 100644
--- a/src/python/doc/installation.rst
+++ b/src/python/doc/installation.rst
@@ -396,7 +396,11 @@ mathematics, science, and engineering.
TensorFlow
----------
-`TensorFlow <https://www.tensorflow.org>`_ is currently only used in some automatic differentiation tests.
+The :doc:`cubical complex </cubical_complex_tflow_itf_ref>`, :doc:`simplex tree </ls_simplex_tree_tflow_itf_ref>`
+and :doc:`Rips complex </rips_complex_tflow_itf_ref>` modules require `TensorFlow <https://www.tensorflow.org>`_
+for incorporating them in neural nets.
+
+`TensorFlow <https://www.tensorflow.org>`_ is also used in some automatic differentiation tests.
Bug reports and contributions
*****************************
diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst
new file mode 100644
index 00000000..9d7d633f
--- /dev/null
+++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst
@@ -0,0 +1,53 @@
+:orphan:
+
+.. To get rid of WARNING: document isn't included in any toctree
+
+TensorFlow layer for lower-star persistence on simplex trees
+############################################################
+
+.. include:: differentiation_sum.inc
+
+Example of gradient computed from lower-star filtration of a simplex tree
+-------------------------------------------------------------------------
+
+.. testcode::
+
+ from gudhi.tensorflow import LowerStarSimplexTreeLayer
+ import tensorflow as tf
+ import gudhi as gd
+
+ st = gd.SimplexTree()
+ st.insert([0, 1])
+ st.insert([1, 2])
+ st.insert([2, 3])
+ st.insert([3, 4])
+ st.insert([4, 5])
+ st.insert([5, 6])
+ st.insert([6, 7])
+ st.insert([7, 8])
+ st.insert([8, 9])
+ st.insert([9, 10])
+
+ F = tf.Variable([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=tf.float32, trainable=True)
+ sl = LowerStarSimplexTreeLayer(simplextree=st, homology_dimensions=[0])
+
+ with tf.GradientTape() as tape:
+ dgm = sl.call(F)[0][0]
+ loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0])))
+
+ grads = tape.gradient(loss, [F])
+ print(grads[0].indices.numpy())
+ print(grads[0].values.numpy())
+
+.. testoutput::
+
+ [2 4]
+ [-1. 1.]
+
+Documentation for LowerStarSimplexTreeLayer
+-------------------------------------------
+
+.. autoclass:: gudhi.tensorflow.LowerStarSimplexTreeLayer
+ :members:
+ :special-members: __init__
+ :show-inheritance:
diff --git a/src/python/doc/rips_complex_sum.inc b/src/python/doc/rips_complex_sum.inc
index 2cb24990..6931ebee 100644
--- a/src/python/doc/rips_complex_sum.inc
+++ b/src/python/doc/rips_complex_sum.inc
@@ -11,4 +11,7 @@
| | | |
+----------------------------------------------------------------+------------------------------------------------------------------------+----------------------------------------------------------------------------------+
| * :doc:`rips_complex_user` | * :doc:`rips_complex_ref` |
- +----------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
+ +----------------------------------------------------------------+------------------------------------------------------------------------+----------------------------------------------------------------------------------+
+ | | * :doc:`rips_complex_tflow_itf_ref` | :requires: `TensorFlow <installation.html#tensorflow>`_ |
+ | | | |
+ +----------------------------------------------------------------+------------------------------------------------------------------------+----------------------------------------------------------------------------------+
diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst
new file mode 100644
index 00000000..3ce75868
--- /dev/null
+++ b/src/python/doc/rips_complex_tflow_itf_ref.rst
@@ -0,0 +1,48 @@
+:orphan:
+
+.. To get rid of WARNING: document isn't included in any toctree
+
+TensorFlow layer for Vietoris-Rips persistence
+##############################################
+
+.. include:: differentiation_sum.inc
+
+Example of gradient computed from Vietoris-Rips persistence
+-----------------------------------------------------------
+
+.. testsetup::
+
+ import numpy
+ numpy.set_printoptions(precision=4)
+
+.. testcode::
+
+ from gudhi.tensorflow import RipsLayer
+ import tensorflow as tf
+
+ X = tf.Variable([[1.,1.],[2.,2.]], dtype=tf.float32, trainable=True)
+ rl = RipsLayer(maximum_edge_length=2., homology_dimensions=[0])
+
+ with tf.GradientTape() as tape:
+ dgm = rl.call(X)[0][0]
+ loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0])))
+
+ grads = tape.gradient(loss, [X])
+ print(grads[0].numpy())
+
+.. testcleanup::
+
+ numpy.set_printoptions(precision=8)
+
+.. testoutput::
+
+ [[-0.5 -0.5]
+ [ 0.5 0.5]]
+
+Documentation for RipsLayer
+---------------------------
+
+.. autoclass:: gudhi.tensorflow.RipsLayer
+ :members:
+ :special-members: __init__
+ :show-inheritance:
diff --git a/src/python/doc/simplex_tree_sum.inc b/src/python/doc/simplex_tree_sum.inc
index a8858f16..3ad1292c 100644
--- a/src/python/doc/simplex_tree_sum.inc
+++ b/src/python/doc/simplex_tree_sum.inc
@@ -1,13 +1,16 @@
.. table::
:widths: 30 40 30
- +----------------------------------------------------------------+------------------------------------------------------------------------+-----------------------------+
- | .. figure:: | The simplex tree is an efficient and flexible data structure for | :Author: Clément Maria |
- | ../../doc/Simplex_tree/Simplex_tree_representation.png | representing general (filtered) simplicial complexes. | |
- | :alt: Simplex tree representation | | :Since: GUDHI 2.0.0 |
- | :figclass: align-center | The data structure is described in | |
- | | :cite:`boissonnatmariasimplextreealgorithmica` | :License: MIT |
- | | | |
- +----------------------------------------------------------------+------------------------------------------------------------------------+-----------------------------+
- | * :doc:`simplex_tree_user` | * :doc:`simplex_tree_ref` |
- +----------------------------------------------------------------+------------------------------------------------------------------------------------------------------+
+ +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | .. figure:: | The simplex tree is an efficient and flexible data structure for | :Author: Clément Maria |
+ | ../../doc/Simplex_tree/Simplex_tree_representation.png | representing general (filtered) simplicial complexes. | |
+ | :alt: Simplex tree representation | | :Since: GUDHI 2.0.0 |
+ | :figclass: align-center | The data structure is described in | |
+ | | :cite:`boissonnatmariasimplextreealgorithmica` | :License: MIT |
+ | | | |
+ +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | * :doc:`simplex_tree_user` | * :doc:`simplex_tree_ref` |
+ +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
+ | | * :doc:`ls_simplex_tree_tflow_itf_ref` | :requires: `TensorFlow <installation.html#tensorflow>`_ |
+ | | | |
+ +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+
diff --git a/src/python/gudhi/tensorflow/__init__.py b/src/python/gudhi/tensorflow/__init__.py
new file mode 100644
index 00000000..1599cf52
--- /dev/null
+++ b/src/python/gudhi/tensorflow/__init__.py
@@ -0,0 +1,5 @@
+from .cubical_layer import CubicalLayer
+from .lower_star_simplex_tree_layer import LowerStarSimplexTreeLayer
+from .rips_layer import RipsLayer
+
+__all__ = ["LowerStarSimplexTreeLayer", "RipsLayer", "CubicalLayer"]
diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py
new file mode 100644
index 00000000..3304e719
--- /dev/null
+++ b/src/python/gudhi/tensorflow/cubical_layer.py
@@ -0,0 +1,82 @@
+import numpy as np
+import tensorflow as tf
+from ..cubical_complex import CubicalComplex
+
+######################
+# Cubical filtration #
+######################
+
+# The parameters of the model are the pixel values.
+
+def _Cubical(Xflat, Xdim, dimensions, homology_coeff_field):
+ # Parameters: Xflat (flattened image),
+ # Xdim (shape of non-flattened image)
+ # dimensions (homology dimensions)
+
+ # Compute the persistence pairs with Gudhi
+ # We reverse the dimensions because CubicalComplex uses Fortran ordering
+ cc = CubicalComplex(dimensions=Xdim[::-1], top_dimensional_cells=Xflat)
+ cc.compute_persistence(homology_coeff_field=homology_coeff_field)
+
+ # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices
+ cof_pp = cc.cofaces_of_persistence_pairs()
+
+ L_cofs = []
+ for dim in dimensions:
+
+ try:
+ cof = cof_pp[0][dim]
+ except IndexError:
+ cof = np.array([])
+
+ L_cofs.append(np.array(cof, dtype=np.int32))
+
+ return L_cofs
+
+class CubicalLayer(tf.keras.layers.Layer):
+ """
+ TensorFlow layer for computing the persistent homology of a cubical complex
+ """
+ def __init__(self, homology_dimensions, min_persistence=None, homology_coeff_field=11, **kwargs):
+ """
+ Constructor for the CubicalLayer class
+
+ Parameters:
+ homology_dimensions (List[int]): list of homology dimensions
+ min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions)
+ homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337.
+ """
+ super().__init__(dynamic=True, **kwargs)
+ self.dimensions = homology_dimensions
+ self.min_persistence = min_persistence if min_persistence != None else [0.] * len(self.dimensions)
+ self.hcf = homology_coeff_field
+ assert len(self.min_persistence) == len(self.dimensions)
+
+ def call(self, X):
+ """
+ Compute persistence diagram associated to a cubical complex filtered by some pixel values
+
+ Parameters:
+ X (TensorFlow variable): pixel values of the cubical complex
+
+ Returns:
+ List[Tuple[tf.Tensor,tf.Tensor]]: List of cubical persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively. Note that the essential part is always empty in cubical persistence diagrams, except in homology dimension zero, where the essential part always contains a single point, with abscissa equal to the smallest value in the complex, and infinite ordinate
+ """
+ # Compute pixels associated to positive and negative simplices
+ # Don't compute gradient for this operation
+ Xflat = tf.reshape(X, [-1])
+ Xdim, Xflat_numpy = X.shape, Xflat.numpy()
+ indices_list = _Cubical(Xflat_numpy, Xdim, self.dimensions, self.hcf)
+ index_essential = np.argmin(Xflat_numpy) # index of minimum pixel value for essential persistence diagram
+ # Get persistence diagram by simply picking the corresponding entries in the image
+ self.dgms = []
+ for idx_dim, dimension in enumerate(self.dimensions):
+ finite_dgm = tf.reshape(tf.gather(Xflat, indices_list[idx_dim]), [-1,2])
+ essential_dgm = tf.reshape(tf.gather(Xflat, index_essential), [-1,1]) if dimension == 0 else tf.zeros([0, 1])
+ min_pers = self.min_persistence[idx_dim]
+ if min_pers >= 0:
+ persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers)
+ self.dgms.append((tf.reshape(tf.gather(finite_dgm, indices=persistent_indices), [-1,2]), essential_dgm))
+ else:
+ self.dgms.append((finite_dgm, essential_dgm))
+ return self.dgms
diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py
new file mode 100644
index 00000000..5a8e5b75
--- /dev/null
+++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py
@@ -0,0 +1,87 @@
+import numpy as np
+import tensorflow as tf
+
+#########################################
+# Lower star filtration on simplex tree #
+#########################################
+
+# The parameters of the model are the vertex function values of the simplex tree.
+
+def _LowerStarSimplexTree(simplextree, filtration, dimensions, homology_coeff_field):
+ # Parameters: simplextree (simplex tree on which to compute persistence)
+ # filtration (function values on the vertices of st),
+ # dimensions (homology dimensions),
+ # homology_coeff_field (homology field coefficient)
+
+ simplextree.reset_filtration(-np.inf, 0)
+
+ # Assign new filtration values
+ for i in range(simplextree.num_vertices()):
+ simplextree.assign_filtration([i], filtration[i])
+ simplextree.make_filtration_non_decreasing()
+
+ # Compute persistence diagram
+ simplextree.compute_persistence(homology_coeff_field=homology_coeff_field)
+
+ # Get vertex pairs for optimization. First, get all simplex pairs
+ pairs = simplextree.lower_star_persistence_generators()
+
+ L_indices = []
+ for dimension in dimensions:
+
+ finite_pairs = pairs[0][dimension] if len(pairs[0]) >= dimension+1 else np.empty(shape=[0,2])
+ essential_pairs = pairs[1][dimension] if len(pairs[1]) >= dimension+1 else np.empty(shape=[0,1])
+
+ finite_indices = np.array(finite_pairs.flatten(), dtype=np.int32)
+ essential_indices = np.array(essential_pairs.flatten(), dtype=np.int32)
+
+ L_indices.append((finite_indices, essential_indices))
+
+ return L_indices
+
+class LowerStarSimplexTreeLayer(tf.keras.layers.Layer):
+ """
+ TensorFlow layer for computing lower-star persistence out of a simplex tree
+ """
+ def __init__(self, simplextree, homology_dimensions, min_persistence=None, homology_coeff_field=11, **kwargs):
+ """
+ Constructor for the LowerStarSimplexTreeLayer class
+
+ Parameters:
+ simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n-1, where n is its number of vertices. Note that its filtration values are modified in each call of the class.
+ homology_dimensions (List[int]): list of homology dimensions
+ min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions)
+ homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337.
+ """
+ super().__init__(dynamic=True, **kwargs)
+ self.dimensions = homology_dimensions
+ self.simplextree = simplextree
+ self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))]
+ self.hcf = homology_coeff_field
+ assert len(self.min_persistence) == len(self.dimensions)
+
+ def call(self, filtration):
+ """
+ Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree
+
+ Parameters:
+ F (TensorFlow variable): filter function values over the vertices of the simplex tree. The ith entry of F corresponds to vertex i in self.simplextree
+
+ Returns:
+ List[Tuple[tf.Tensor,tf.Tensor]]: List of lower-star persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively
+ """
+ # Don't try to compute gradients for the vertex pairs
+ indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions, self.hcf)
+ # Get persistence diagrams
+ self.dgms = []
+ for idx_dim, dimension in enumerate(self.dimensions):
+ finite_dgm = tf.reshape(tf.gather(filtration, indices[idx_dim][0]), [-1,2])
+ essential_dgm = tf.reshape(tf.gather(filtration, indices[idx_dim][1]), [-1,1])
+ min_pers = self.min_persistence[idx_dim]
+ if min_pers >= 0:
+ persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers)
+ self.dgms.append((tf.reshape(tf.gather(finite_dgm, indices=persistent_indices),[-1,2]), essential_dgm))
+ else:
+ self.dgms.append((finite_dgm, essential_dgm))
+ return self.dgms
+
diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py
new file mode 100644
index 00000000..2a73472c
--- /dev/null
+++ b/src/python/gudhi/tensorflow/rips_layer.py
@@ -0,0 +1,93 @@
+import numpy as np
+import tensorflow as tf
+from ..rips_complex import RipsComplex
+
+############################
+# Vietoris-Rips filtration #
+############################
+
+# The parameters of the model are the point coordinates.
+
+def _Rips(DX, max_edge, dimensions, homology_coeff_field):
+ # Parameters: DX (distance matrix),
+ # max_edge (maximum edge length for Rips filtration),
+ # dimensions (homology dimensions)
+
+ # Compute the persistence pairs with Gudhi
+ rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge)
+ st = rc.create_simplex_tree(max_dimension=max(dimensions)+1)
+ st.compute_persistence(homology_coeff_field=homology_coeff_field)
+ pairs = st.flag_persistence_generators()
+
+ L_indices = []
+ for dimension in dimensions:
+
+ if dimension == 0:
+ finite_pairs = pairs[0]
+ essential_pairs = pairs[2]
+ else:
+ finite_pairs = pairs[1][dimension-1] if len(pairs[1]) >= dimension else np.empty(shape=[0,4])
+ essential_pairs = pairs[3][dimension-1] if len(pairs[3]) >= dimension else np.empty(shape=[0,2])
+
+ finite_indices = np.array(finite_pairs.flatten(), dtype=np.int32)
+ essential_indices = np.array(essential_pairs.flatten(), dtype=np.int32)
+
+ L_indices.append((finite_indices, essential_indices))
+
+ return L_indices
+
+class RipsLayer(tf.keras.layers.Layer):
+ """
+ TensorFlow layer for computing Rips persistence out of a point cloud
+ """
+ def __init__(self, homology_dimensions, maximum_edge_length=np.inf, min_persistence=None, homology_coeff_field=11, **kwargs):
+ """
+ Constructor for the RipsLayer class
+
+ Parameters:
+ maximum_edge_length (float): maximum edge length for the Rips complex
+ homology_dimensions (List[int]): list of homology dimensions
+ min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions)
+ homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337.
+ """
+ super().__init__(dynamic=True, **kwargs)
+ self.max_edge = maximum_edge_length
+ self.dimensions = homology_dimensions
+ self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))]
+ self.hcf = homology_coeff_field
+ assert len(self.min_persistence) == len(self.dimensions)
+
+ def call(self, X):
+ """
+ Compute Rips persistence diagram associated to a point cloud
+
+ Parameters:
+ X (TensorFlow variable): point cloud of shape [number of points, number of dimensions]
+
+ Returns:
+ List[Tuple[tf.Tensor,tf.Tensor]]: List of Rips persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively
+ """
+ # Compute distance matrix
+ DX = tf.norm(tf.expand_dims(X, 1)-tf.expand_dims(X, 0), axis=2)
+ # Compute vertices associated to positive and negative simplices
+ # Don't compute gradient for this operation
+ indices = _Rips(DX.numpy(), self.max_edge, self.dimensions, self.hcf)
+ # Get persistence diagrams by simply picking the corresponding entries in the distance matrix
+ self.dgms = []
+ for idx_dim, dimension in enumerate(self.dimensions):
+ cur_idx = indices[idx_dim]
+ if dimension > 0:
+ finite_dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(cur_idx[0], [-1,2])), [-1,2])
+ essential_dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(cur_idx[1], [-1,2])), [-1,1])
+ else:
+ reshaped_cur_idx = tf.reshape(cur_idx[0], [-1,3])
+ finite_dgm = tf.concat([tf.zeros([reshaped_cur_idx.shape[0],1]), tf.reshape(tf.gather_nd(DX, reshaped_cur_idx[:,1:]), [-1,1])], axis=1)
+ essential_dgm = tf.zeros([cur_idx[1].shape[0],1])
+ min_pers = self.min_persistence[idx_dim]
+ if min_pers >= 0:
+ persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers)
+ self.dgms.append((tf.reshape(tf.gather(finite_dgm, indices=persistent_indices),[-1,2]), essential_dgm))
+ else:
+ self.dgms.append((finite_dgm, essential_dgm))
+ return self.dgms
+
diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py
new file mode 100644
index 00000000..dca001a9
--- /dev/null
+++ b/src/python/test/test_diff.py
@@ -0,0 +1,78 @@
+from gudhi.tensorflow import *
+import numpy as np
+import tensorflow as tf
+import gudhi as gd
+
+def test_rips_diff():
+
+ Xinit = np.array([[1.,1.],[2.,2.]], dtype=np.float32)
+ X = tf.Variable(initial_value=Xinit, trainable=True)
+ rl = RipsLayer(maximum_edge_length=2., homology_dimensions=[0])
+
+ with tf.GradientTape() as tape:
+ dgm = rl.call(X)[0][0]
+ loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0])))
+ grads = tape.gradient(loss, [X])
+ assert tf.norm(grads[0]-tf.constant([[-.5,-.5],[.5,.5]]),1) <= 1e-6
+
+def test_cubical_diff():
+
+ Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32)
+ X = tf.Variable(initial_value=Xinit, trainable=True)
+ cl = CubicalLayer(homology_dimensions=[0])
+
+ with tf.GradientTape() as tape:
+ dgm = cl.call(X)[0][0]
+ loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0])))
+ grads = tape.gradient(loss, [X])
+ assert tf.norm(grads[0]-tf.constant([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]]),1) <= 1e-6
+
+def test_nonsquare_cubical_diff():
+
+ Xinit = np.array([[-1.,1.,0.],[1.,1.,1.]], dtype=np.float32)
+ X = tf.Variable(initial_value=Xinit, trainable=True)
+ cl = CubicalLayer(homology_dimensions=[0])
+
+ with tf.GradientTape() as tape:
+ dgm = cl.call(X)[0][0]
+ loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0])))
+ grads = tape.gradient(loss, [X])
+ assert tf.norm(grads[0]-tf.constant([[0.,0.5,-0.5],[0.,0.,0.]]),1) <= 1e-6
+
+def test_st_diff():
+
+ st = gd.SimplexTree()
+ st.insert([0])
+ st.insert([1])
+ st.insert([2])
+ st.insert([3])
+ st.insert([4])
+ st.insert([5])
+ st.insert([6])
+ st.insert([7])
+ st.insert([8])
+ st.insert([9])
+ st.insert([10])
+ st.insert([0, 1])
+ st.insert([1, 2])
+ st.insert([2, 3])
+ st.insert([3, 4])
+ st.insert([4, 5])
+ st.insert([5, 6])
+ st.insert([6, 7])
+ st.insert([7, 8])
+ st.insert([8, 9])
+ st.insert([9, 10])
+
+ Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32)
+ F = tf.Variable(initial_value=Finit, trainable=True)
+ sl = LowerStarSimplexTreeLayer(simplextree=st, homology_dimensions=[0])
+
+ with tf.GradientTape() as tape:
+ dgm = sl.call(F)[0][0]
+ loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0])))
+ grads = tape.gradient(loss, [F])
+
+ assert tf.math.reduce_all(tf.math.equal(grads[0].indices, tf.constant([2,4])))
+ assert tf.math.reduce_all(tf.math.equal(grads[0].values, tf.constant([-1.,1.])))
+