--- a/.hgignore Fri Aug 24 16:58:25 2007 -0400
+++ b/.hgignore Tue Jun 27 09:37:05 2017 -0700
@@ -3,3 +3,4 @@
docs
aux
build
+doc/.build
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/.filters Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,11 @@
+[kinetic]
+category=kinetic
+
+[functionality]
+category=functionality
+
+[installation]
+category=installation
+
+[efficiency]
+category=efficiency
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/89ae955518665a61/new/1221008555.M726241P30017Q23.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,15 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Tue, 26 Feb 2008 18:22:06 -0500
+State: new
+Subject: Get rid of intostring() and .c_str()
+Message-Id: <89ae955518665a61-0-artemis@metatron>
+
+Get rid of the need for intostring() (in addition to tostring()), and having to
+place .c_str() after tostring() in rLog calls.
+
+The former is necessary because of some problem with disambiguating which
+operator<<(ostream,T) to use when Event is being output, so intostring() calls
+T.operator<<(ostream) explicitly. This problem seems to exist only for Events.
+
+It should be possible to solve the latter by returning char* from tostring()
+rather than std::string.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/8a14b4849071f910/new/1221008555.M667846P30017Q17.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Thu, 10 Jan 2008 04:36:03 -0500
+State: resolved
+Subject: Remove maintenance of "lazy decomposition"
+Message-Id: <8a14b4849071f910-0-artemis@metatron>
+resolution: fixed
+
+The maintenance of "lazy decomposition" (added in [a0736dd3c671]) is
+incorrect (due to original theoretical errors). Remove it completely.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/8a14b4849071f910/new/1229837212.M576947P31212Q2.rufus Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Sat, 20 Dec 2008 21:26:52
+Subject: properties changes (state, resolution)
+Message-Id: <8a14b4849071f910-c82a77e7b320c7e8-artemis@rufus>
+References: <8a14b4849071f910-0-artemis@metatron>
+In-Reply-To: <8a14b4849071f910-0-artemis@metatron>
+
+state=resolved
+resolution=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/8aa25bcae639fc99/new/1221008555.M674695P30017Q18.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Fri, 04 Apr 2008 11:18:00
+State: fixed
+Subject: Switch in the pairing in Case 1.2
+Message-Id: <8aa25bcae639fc99-0-artemis@moscow>
+
+If simplex i is unpaired in Case 1.2, while i+1 is paired with l, and R[i,l]=1
+there is a switch in the pairing in Case 1.2.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/8aa25bcae639fc99/new/1221008555.M677989P30017Q19.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Fri, 04 Apr 2008 16:16:41
+Subject: Handled but mislabeled
+Message-Id: <8aa25bcae639fc99-bbca170318887f06-artemis@moscow>
+References: <8aa25bcae639fc99-0-artemis@moscow>
+In-Reply-To: <8aa25bcae639fc99-0-artemis@moscow>
+
+The situation is already handled in the code, but was mislabeled as Case 1.1.2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/8aa25bcae639fc99/new/1221008555.M679929P30017Q20.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Fri, 04 Apr 2008 16:17:10
+Subject: properties changes (state)
+Message-Id: <8aa25bcae639fc99-a4a82c4ab8c0f536-artemis@moscow>
+References: <8aa25bcae639fc99-0-artemis@moscow>
+In-Reply-To: <8aa25bcae639fc99-0-artemis@moscow>
+
+state=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/91986229a564f7e0/new/1221008555.M732706P30017Q24.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Thu, 21 Feb 2008 04:23:29 -0500
+State: fixed
+Subject: More elabroate domain in avida-landscape
+Message-Id: <91986229a564f7e0-0-artemis@metatron>
+
+Try minimum spanning tree (i.e., take only negative edges from avida-distance).
+Try a combination: all edges up to a certain (realtively small) length + MST.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/91986229a564f7e0/new/1221008555.M735706P30017Q25.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Mon, 25 Feb 2008 07:09:30 -0500
+Subject: properties changes (state)
+Message-Id: <91986229a564f7e0-9886ad2e18d12feb-artemis@metatron>
+References: <91986229a564f7e0-0-artemis@metatron>
+In-Reply-To: <91986229a564f7e0-0-artemis@metatron>
+
+state=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/957a589d7c6c3fa8/new/1230356422.M300560P11955Q1.rufus Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,11 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Fri, 26 Dec 2008 21:34:29
+State: new
+Subject: namespace dionysus
+Message-Id: <957a589d7c6c3fa8-0-artemis@rufus>
+category: installation
+
+Put everything in namespace dionysus. Besides making things more organized, it
+will also allow us to get rid of the problem of having to typedef Simplex<...>
+to Smplx. If it were in dionysus namespace, then
+`typedef dionysus::Simplex<...> Simplex;` would work.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/957a589d7c6c3fa8/new/1239235561.M108369P10645Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Wed, 08 Apr 2009 17:06:01
+Subject: changed properties (category=installation)
+Message-Id: <957a589d7c6c3fa8-e9829eb7352224cf-artemis@cole>
+References: <957a589d7c6c3fa8-0-artemis@rufus>
+In-Reply-To: <957a589d7c6c3fa8-0-artemis@rufus>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/96b51b44f7764f5c/new/1221008595.M830007P30168Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Tue, 09 Sep 2008 18:03:00
+State: resolved
+Subject: Boost 1.36
+Message-Id: <96b51b44f7764f5c-0-artemis@cole>
+resolution: fixed
+
+Make the software compile with Boost 1.36
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/96b51b44f7764f5c/new/1229837196.M796227P31208Q2.rufus Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Sat, 20 Dec 2008 21:26:36
+Subject: properties changes (state, resolution)
+Message-Id: <96b51b44f7764f5c-85f856488e2d4827-artemis@rufus>
+References: <96b51b44f7764f5c-0-artemis@cole>
+In-Reply-To: <96b51b44f7764f5c-0-artemis@cole>
+
+state=resolved
+resolution=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/b5d8a8403ae3a0d2/new/1221008555.M687915P30017Q21.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Thu, 21 Feb 2008 04:54:00 -0500
+State: fixed
+Subject: Segfault in tests/geometry/test-kinetic-sort.cpp
+Message-Id: <b5d8a8403ae3a0d2-0-artemis@metatron>
+
+test-kinetic-sort segfaults when we subscribe to geometry/simulator RLog channel.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/b5d8a8403ae3a0d2/new/1221008555.M690386P30017Q22.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Fri, 22 Feb 2008 13:05:55 -0500
+Subject: properties changes (state)
+Message-Id: <b5d8a8403ae3a0d2-a0288cca74095157-artemis@metatron>
+References: <b5d8a8403ae3a0d2-0-artemis@metatron>
+In-Reply-To: <b5d8a8403ae3a0d2-0-artemis@metatron>
+
+state=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c420501cc5285bbc/new/1221008555.M610599P30017Q9.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Tue, 26 Feb 2008 05:22:56 -0500
+State: fixed
+Subject: Non-optimized CGAL runtime error
+Message-Id: <c420501cc5285bbc-0-artemis@metatron>
+
+If the code is compiled with optimizations off or with debug on, CGAL gives a
+runtime error.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c420501cc5285bbc/new/1221008555.M614720P30017Q10.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Tue, 26 Feb 2008 08:00:23 -0500
+Subject: properties changes (state)
+Message-Id: <c420501cc5285bbc-e983173b6cd399a6-artemis@metatron>
+References: <c420501cc5285bbc-0-artemis@metatron>
+In-Reply-To: <c420501cc5285bbc-0-artemis@metatron>
+
+state=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c420501cc5285bbc/new/1221008555.M618213P30017Q11.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,10 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Tue, 26 Feb 2008 08:00:50 -0500
+Subject: Fixed by adding CGAL_NO_ASSERTIONS
+Message-Id: <c420501cc5285bbc-d75d85d67d421f7e-artemis@metatron>
+References: <c420501cc5285bbc-0-artemis@metatron>
+In-Reply-To: <c420501cc5285bbc-0-artemis@metatron>
+
+I suspect the bug in CGAL (since everything works fine with CGAL_NO_ASSERTIONS
+set), so I disabled the offending assertion (and all the rest of them) by
+setting a CXX flag in CMakeLists.txt.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c420501cc5285bbc/new/1221008555.M619987P30017Q12.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Wed, 19 Mar 2008 12:47:24 -0400
+Subject: Fixed in 0a18d6902a55
+Message-Id: <c420501cc5285bbc-db67b15857938f7b-artemis@metatron>
+References: <c420501cc5285bbc-0-artemis@metatron>
+In-Reply-To: <c420501cc5285bbc-0-artemis@metatron>
+
+Fixed by dealing with infinite simplices in 0a18d6902a55
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c420501cc5285bbc/new/1221008555.M621758P30017Q13.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Wed, 19 Mar 2008 12:49:01 -0400
+Subject: Reopened for ar-vineyard
+Message-Id: <c420501cc5285bbc-8f74ccde6b3f0bea-artemis@metatron>
+References: <c420501cc5285bbc-0-artemis@metatron>
+In-Reply-To: <c420501cc5285bbc-0-artemis@metatron>
+
+Fix for ar-vineyard like for alphashapes
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c420501cc5285bbc/new/1221008555.M623619P30017Q14.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Wed, 19 Mar 2008 12:49:47 -0400
+Subject: properties changes (state)
+Message-Id: <c420501cc5285bbc-ef5f506b45aba335-artemis@metatron>
+References: <c420501cc5285bbc-0-artemis@metatron>
+In-Reply-To: <c420501cc5285bbc-0-artemis@metatron>
+
+state=open
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c420501cc5285bbc/new/1221008555.M625407P30017Q15.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Wed, 19 Mar 2008 13:17:30 -0400
+Subject: Fixed for ar-vineyard
+Message-Id: <c420501cc5285bbc-feb676745fc16b8c-artemis@metatron>
+References: <c420501cc5285bbc-8f74ccde6b3f0bea-artemis@metatron>
+In-Reply-To: <c420501cc5285bbc-8f74ccde6b3f0bea-artemis@metatron>
+
+Fixed for ar-vineyard.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c664b2f69b5f6ea3/new/1221008555.M741909P30017Q26.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Thu, 28 Feb 2008 05:33:34 -0500
+State: fixed
+Subject: Two simulators
+Message-Id: <c664b2f69b5f6ea3-0-artemis@metatron>
+
+Consider using two simulators instead of one to make sure that trajectory
+changes are processed before simplex swaps.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c664b2f69b5f6ea3/new/1221008555.M745065P30017Q27.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Sat, 01 Mar 2008 04:45:05 -0500
+Subject: properties changes (state)
+Message-Id: <c664b2f69b5f6ea3-f8bf16bdff01098d-artemis@metatron>
+References: <c664b2f69b5f6ea3-0-artemis@metatron>
+In-Reply-To: <c664b2f69b5f6ea3-0-artemis@metatron>
+
+state=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/c664b2f69b5f6ea3/new/1221008555.M746854P30017Q28.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Sat, 01 Mar 2008 04:45:29 -0500
+Subject: Fixed in f236c7d659d0
+Message-Id: <c664b2f69b5f6ea3-a1742f0eb7b5e1c0-artemis@metatron>
+References: <c664b2f69b5f6ea3-0-artemis@metatron>
+In-Reply-To: <c664b2f69b5f6ea3-0-artemis@metatron>
+
+Fixed in f236c7d659d0.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/cd79223a108d3900/new/1221008555.M511949P30017Q2.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Fri, 22 Feb 2008 18:39:31 -0500
+State: new
+Subject: Incorrect sign_at() in UPolynomial<double>
+Message-Id: <cd79223a108d3900-0-artemis@metatron>
+category: kinetic
+
+UPolynomial<double>::sign_at() reports -1 as the sign of -2*x + 4 at x = 2.
+Should be 0.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/cd79223a108d3900/new/1239235469.M178107P10641Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Wed, 08 Apr 2009 17:04:29
+Subject: changed properties (category=kinetic)
+Message-Id: <cd79223a108d3900-8e019293f49e27ce-artemis@cole>
+References: <cd79223a108d3900-0-artemis@metatron>
+In-Reply-To: <cd79223a108d3900-0-artemis@metatron>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/cff004eda75a26c2/new/1221008555.M586932P30017Q7.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Tue, 11 Mar 2008 04:05:25 -0400
+State: fixed
+Subject: Scale fitness
+Message-Id: <cff004eda75a26c2-0-artemis@metatron>
+
+When computing fitness persistence, divide all values by the maximum fitness.
+That way it's possible to compare persistence diagrams for runs in different
+environments.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/cff004eda75a26c2/new/1221008555.M591027P30017Q8.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Fri, 14 Mar 2008 18:34:41 -0400
+Subject: properties changes (state)
+Message-Id: <cff004eda75a26c2-ad10cae14deb092d-artemis@metatron>
+References: <cff004eda75a26c2-0-artemis@metatron>
+In-Reply-To: <cff004eda75a26c2-0-artemis@metatron>
+
+state=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/d2ab07329c3588ca/new/1221008555.M528839P30017Q3.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Wed, 27 Feb 2008 16:30:57 -0500
+State: fixed
+Subject: Segfault with topology/vineyard log enabled
+Message-Id: <d2ab07329c3588ca-0-artemis@metatron>
+
+The code segfaults in Vineyard::record_knee() if we subscribe to
+topology/vineyard log.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/d2ab07329c3588ca/new/1221008555.M531575P30017Q4.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Sat, 01 Mar 2008 04:46:46 -0500
+Subject: properties changes (state)
+Message-Id: <d2ab07329c3588ca-8c184b421f538482-artemis@metatron>
+References: <d2ab07329c3588ca-0-artemis@metatron>
+In-Reply-To: <d2ab07329c3588ca-0-artemis@metatron>
+
+state=fixed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/d2ab07329c3588ca/new/1221008555.M533535P30017Q5.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,8 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Sat, 01 Mar 2008 04:46:51 -0500
+Subject: Fixed in abba2950aced
+Message-Id: <d2ab07329c3588ca-fa65c35dfff6366f-artemis@metatron>
+References: <d2ab07329c3588ca-0-artemis@metatron>
+In-Reply-To: <d2ab07329c3588ca-0-artemis@metatron>
+
+Fixed in abba2950aced
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/de674a2ac5f6c18c/new/1229837097.M859247P31105Q1.rufus Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,11 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Sat, 20 Dec 2008 21:21:53
+State: new
+Subject: Specialize ChainWrapper<C>::add() for linked lists
+Message-Id: <de674a2ac5f6c18c-0-artemis@rufus>
+category: functionality
+
+The current implementation of add() in ChainWrapper is container agnostic: it
+uses a temporary container, and then swaps it into place. There should be a
+specialized add() for linked lists (in particular, List from circular_list.h)
+since it is would likely be more efficient then the generic one.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/de674a2ac5f6c18c/new/1239235533.M711045P10644Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Wed, 08 Apr 2009 17:05:33
+Subject: changed properties (category=functionality)
+Message-Id: <de674a2ac5f6c18c-ba280aee1f76cd3f-artemis@cole>
+References: <de674a2ac5f6c18c-0-artemis@rufus>
+In-Reply-To: <de674a2ac5f6c18c-0-artemis@rufus>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/e6d9deee8fbd81a5/new/1221008555.M541088P30017Q6.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Wed, 06 Feb 2008 16:45:13 -0500
+State: new
+Subject: About section in README
+Message-Id: <e6d9deee8fbd81a5-0-artemis@metatron>
+
+Add "About" section to the README file.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/e6f52c44ef26f4a7/new/1239399291.M841173P26652Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,10 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Fri, 10 Apr 2009 14:33:18
+State: new
+Subject: Intrusive containers for ZigzagPersistence
+Message-Id: <e6f52c44ef26f4a7-0-artemis@cole>
+category: efficiency
+
+Use Boost's intrusive containers to store rows in ZigzagPersistence. This
+should get rid of the inefficiencies associated with the simplex removal
+(through ZigzagPersistence::remove()).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/e8659770a6824e01/new/1221008555.M475248P30017Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,10 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Wed, 09 Jan 2008 13:57:57 -0500
+State: new
+Subject: Change cout to rlog
+Message-Id: <e8659770a6824e01-0-artemis@metatron>
+
+In examples/grid/grid2Dvineyard.hpp (and in examples in general),
+change the use of std::cout for logging information to rlog.
+Naturally, create appropriate channels even if they are of only local
+use.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/ebda8db3f9908e33/new/1221008555.M637662P30017Q16.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <morozov@cs.duke.edu>
+Date: Mon, 25 Feb 2008 11:29:27 -0500
+State: resolved
+Subject: Efficient EventQueue
+Message-Id: <ebda8db3f9908e33-0-artemis@metatron>
+category: kinetic
+resolution: fixed
+
+Change EventQueue to an efficient implementation, e.g., using a Fibonacci heap.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/ebda8db3f9908e33/new/1239235517.M200551P10643Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Wed, 08 Apr 2009 17:05:17
+Subject: changed properties (category=kinetic)
+Message-Id: <ebda8db3f9908e33-f094f3f4c111054c-artemis@cole>
+References: <ebda8db3f9908e33-0-artemis@metatron>
+In-Reply-To: <ebda8db3f9908e33-0-artemis@metatron>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/ebda8db3f9908e33/new/1265568370.M269870P17954Q1.vine Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Sun, 07 Feb 2010 10:45:04
+Subject: Switched to Boost's MultiIndex
+Message-Id: <ebda8db3f9908e33-e9c51216e26b5e19-artemis@vine>
+References: <ebda8db3f9908e33-0-artemis@metatron>
+In-Reply-To: <ebda8db3f9908e33-0-artemis@metatron>
+
+Switched EventQueue to Boost's MultiIndex with a sequence and ordered_non_unique
+indexing.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/ebda8db3f9908e33/new/1265605942.M759644P4728Q1.vine Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Sun, 07 Feb 2010 21:11:12
+Subject: Binary heap
+Message-Id: <ebda8db3f9908e33-e780beb990f99293-artemis@vine>
+References: <ebda8db3f9908e33-0-artemis@metatron>
+In-Reply-To: <ebda8db3f9908e33-0-artemis@metatron>
+
+Changed EventQueue representation to a binary heap. Used code developed by Danny
+Tarlow and Yanbin Lu.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/f6496b3e37275888/new/1229837161.M623187P31203Q1.rufus Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,11 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Sat, 20 Dec 2008 21:24:59
+State: new
+Subject: Add field arithmetic
+Message-Id: <f6496b3e37275888-0-artemis@rufus>
+category: functionality
+
+Add support for field arithmetic (i.e., not only Z_2 field as it is now).
+However, make sure that there is a specialization of ChainWrapper<C>::add() for
+Z_2 field (since it can be made much more efficient both in terms of space and
+time).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.issues/f6496b3e37275888/new/1239235438.M855747P10635Q1.cole Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+From: Dmitriy Morozov <dmitriy@mrzv.org>
+Date: Wed, 08 Apr 2009 17:03:58
+Subject: changed properties (category=functionality)
+Message-Id: <f6496b3e37275888-937e7690680da5fe-artemis@cole>
+References: <f6496b3e37275888-0-artemis@rufus>
+In-Reply-To: <f6496b3e37275888-0-artemis@rufus>
+
--- a/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,95 +1,88 @@
-project (Dionysus)
+project (Dionysus)
+cmake_minimum_required (VERSION 2.4)
-option (debug "Build Dionysus with debugging on" OFF)
-option (counters "Build Dionysus with counters on" OFF)
-option (optimize "Build Dionysus with optimization" ON)
+option (logging "Build Dionysus with logging on" OFF)
+option (counters "Build Dionysus with counters on" OFF)
+option (debug "Build Dionysus with debugging on" OFF)
+option (optimize "Build Dionysus with optimization" ON)
+option (use_cgal "Build examples and python bindings that use CGAL" ON)
+option (use_dsrpdb "Build examples that use DSR-PDB" OFF)
+option (use_synaps "Build examples that use SYNAPS" OFF)
# Find everything that's always required
-find_package (Boost REQUIRED)
-find_package (Doxygen)
-find_library (dsrpdb_LIBRARY NAMES dsrpdb)
-find_path (dsrpdb_INCLUDE_DIR dsrpdb/Protein.h)
-
-set (libraries ${libraries}
- ${dsrpdb_LIBRARY})
+find_package (Boost REQUIRED COMPONENTS program_options python serialization signals)
+find_package (Doxygen)
+if (use_dsrpdb)
+ find_library (dsrpdb_LIBRARY NAMES dsrpdb)
+ find_path (dsrpdb_INCLUDE_DIR dsrpdb/Protein.h)
+endif (use_dsrpdb)
-#CGAL
-execute_process (COMMAND ${CMAKE_MAKE_PROGRAM} -f ${CMAKE_CURRENT_SOURCE_DIR}/FindCGAL.Makefile libpaths
- OUTPUT_VARIABLE cgal_libpaths)
-execute_process (COMMAND ${CMAKE_MAKE_PROGRAM} -f ${CMAKE_CURRENT_SOURCE_DIR}/FindCGAL.Makefile ldflags
- OUTPUT_VARIABLE cgal_ldflags)
-execute_process (COMMAND ${CMAKE_MAKE_PROGRAM} -f ${CMAKE_CURRENT_SOURCE_DIR}/FindCGAL.Makefile cxxflags
- OUTPUT_VARIABLE cgal_cxxflags)
-execute_process (COMMAND ${CMAKE_MAKE_PROGRAM} -f ${CMAKE_CURRENT_SOURCE_DIR}/FindCGAL.Makefile libpath
- OUTPUT_VARIABLE cgal_libpath)
-#string (REPLACE "\n" "" cgal_libpaths ${cgal_libpaths})
-#string (REPLACE "\n" "" cgal_ldflags ${cgal_ldflags})
-string (REPLACE "\n" "" cgal_cxxflags ${cgal_cxxflags})
-string (REPLACE "\n" "" cgal_libpath ${cgal_libpath})
-add_definitions (${cgal_cxxflags})
-find_library (cgal_LIBRARY NAMES CGAL
- PATHS ${cgal_libpath})
-find_library (core_LIBRARY NAMES CGALcore++
- PATHS ${cgal_libpath})
-find_library (mpfr_LIBRARY NAMES mpfr)
-find_library (gmp_LIBRARY NAMES gmp)
-find_library (gmpxx_LIBRARY NAMES gmpxx)
-find_library (m_LIBRARY NAMES m)
-
-set (cgal_libraries ${cgal_LIBRARY}
- ${core_LIBRARY}
- ${mpfr_LIBRARY}
- ${gmp_LIBRARY}
- ${gmpxx_LIBRARY}
- ${m_LIBRARY})
+# CGAL
+if (use_cgal)
+ find_package (CGAL QUIET)
+endif (use_cgal)
+#add_definitions (-DCGAL_NO_ASSERTIONS -DCGAL_NO_PRECONDITIONS)
# SYNAPS
-add_definitions (-DBOOST_UBLAS_TYPE_CHECK=0)
-find_library (synaps_LIBRARY NAMES synaps)
-set (synaps_libraries ${synaps_LIBRARY}
- ${gmp_LIBRARY}
- ${gmpxx_LIBRARY})
+if (use_synaps)
+ add_definitions (-DBOOST_UBLAS_TYPE_CHECK=0)
+ find_library (synaps_LIBRARY NAMES synaps)
+ set (synaps_libraries ${synaps_LIBRARY}
+ ${gmp_LIBRARY}
+ ${gmpxx_LIBRARY})
+endif (use_synaps)
# Debugging
-if (debug)
- find_library (cwd_LIBRARY NAMES cwd)
- find_path (cwd_INCLUDE_DIR libcwd/debug.h)
- set (cwd_INCLUDE_DIR ${cwd_INCLUDE_DIR}/libcwd)
- add_definitions (-DCWDEBUG -g)
- set (external_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/debug.cpp)
- set (libraries ${libraries} ${cwd_LIBRARY})
-else (debug)
- add_definitions (-DNDEBUG)
-endif (debug)
+if (debug)
+ if (optimize)
+ set (cxx_flags ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
+ else (optimize)
+ set (cxx_flags ${CMAKE_CXX_FLAGS_DEBUG})
+ endif (optimize)
+else (debug)
+ if (optimize)
+ set (cxx_flags ${CMAKE_CXX_FLAGS_RELEASE})
+ else (optimize)
+ set (cxx_flags ${CMAKE_CXX_FLAGS})
+ endif (optimize)
+endif (debug)
+add_definitions (${cxx_flags})
+
+# Fix the XCode bug
+add_definitions (-ftemplate-depth=256)
+
+
+# Logging
+if (logging)
+ find_library (rlog_LIBRARY NAMES rlog)
+ find_path (rlog_INCLUDE_DIR rlog/rlog.h)
+ set (rlog_INCLUDE_DIR ${rlog_INCLUDE_DIR})
+ add_definitions (-DLOGGING -DRLOG_COMPONENT=dionysus)
+ set (libraries ${libraries} ${rlog_LIBRARY})
+endif (logging)
# Counters
-if (counters)
- add_definitions (-DCOUNTERS)
-endif (counters)
-
-# Optimization
-if (optimize GREATER 0)
- add_definitions (-O${optimize})
-elseif (optimize)
- add_definitions (-O)
-endif (optimize)
-endif (optimize GREATER 0)
-
+if (counters)
+ add_definitions (-DCOUNTERS)
+endif (counters)
# Set includes
-include_directories (${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/include
- ${Boost_INCLUDE_DIR}
- ${dsrpdb_INCLUDE_DIR}
- ${cwd_INCLUDE_DIR})
+include_directories (${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${Boost_INCLUDE_DIR}
+ ${dsrpdb_INCLUDE_DIR}
+ ${cwd_INCLUDE_DIR}
+ ${rlog_INCLUDE_DIR})
# Doxygen (FIXME)
-if (DOXYGEN_FOUND)
-# add_custom_target (docs ALL
-# ${DOXYGEN_EXECUTABLE} Doxyfile
-# DEPENDS Doxyfile)
-endif (DOXYGEN_FOUND)
+if (DOXYGEN_FOUND)
+# add_custom_target (docs ALL
+# ${DOXYGEN_EXECUTABLE} Doxyfile
+# DEPENDS Doxyfile)
+endif (DOXYGEN_FOUND)
-# Set external sources
-add_subdirectory (examples)
-add_subdirectory (tests)
+# Process subdirectories
+add_subdirectory (examples)
+add_subdirectory (tests)
+add_subdirectory (tools)
+add_subdirectory (bindings)
--- a/FindCGAL.Makefile Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-include $(CGAL_MAKEFILE)
-
-all: libpath libpaths ldflags cxxflags
-
-libpath:
- @echo "$(CGAL_LIB_DIR)"
-
-libpaths:
- @echo "$(CGAL_LIBPATHFLAGS)"
-
-ldflags:
- @echo "$(CGAL_LDFLAGS)"
-
-cxxflags:
- @echo "$(CGAL_CXXFLAGS)"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LEGAL Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,15 @@
+Copyright (C) 2006-2017 Dmitriy Morozov <dmitriy@mrzv.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,280 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
--- a/README Fri Aug 24 16:58:25 2007 -0400
+++ b/README Tue Jun 27 09:37:05 2017 -0700
@@ -1,28 +1,42 @@
-Dependencies
- CGAL-3.3 - for alpha-shapes and kinetic data structures
- DSR-PDB - for reading in PDB files
- cmake - for controlling the build process
- boost - great set of C++ libraries
- Doxygen - for building documentation
- libcwd - for debugging only (is not needed by default)
- synaps - for solving polynomial (for kinetic kernel), which in turn requires GMP
+% Dionysus README
+% Dmitriy Morozov
+
+## Dependencies
-Configuration
- The path to CGAL's Makefile is expected to be set in $CGAL_MAKEFILE, the rest
- is just usual CMake configuration
+ * [CGAL]-3.4 --- for alpha-shapes and kinetic data structures
+ * [DSR-PDB] --- for reading in PDB files (if use_dsrpdb is turned on in cmake)
+ * [CMake] --- for controlling the build process
+ * [boost] --- great set of C++ libraries
+ * [Doxygen] --- for building documentation
+ * [rlog] --- for logging only (is not needed by default)
+ * [SYNAPS] --- for solving polynomials (for kinetic kernel), which in turn requires GMP
-Building
+[CGAL]: http://www.cgal.org
+[DSR-PDB]: http://www.salilab.org/~drussel/pdb/
+[CMake]: http://www.cmake.org
+[boost]: http://www.boost.org
+[Doxygen]: http://www.stack.nl/~dimitri/doxygen/
+[rlog]: http://www.arg0.net/rlog
+[SYNAPS]: http://synaps.inria.fr/
+
+## Building
To build examples, create a directory build (to keep everything in one place),
go to that directory and run cmake and make:
- mkdir build
- cd build
- cmake .. (or "ccmake .." if you want a curses interface)
- make
+
+ mkdir build
+ cd build
+ cmake .. (or "ccmake .." if you want a curses interface)
+ make
- In the cmake line you can provide -Ddebug:bool=on to turn on debugging,
- -Dcounters:bool=on to turn on counters, -Doptimize:int=3 would set
- optimization to -O3. All of this can be set using a text user interface by
- running ccmake instead of cmake.
+ In the cmake line you can provide `-Ddebug:bool=on` to turn on debugging,
+ `-Dcounters:bool=on` to turn on counters, `-Doptimize:bool=on` to turn on
+ optimization, `-Duse_dsrpdb:bool=on` to turn on reading PDB files. Depending on
+ the combination of debugging and optimization, a particular `CMAKE_CXX_FLAGS*`
+ is chosen. All of this can be set using a text user interface by running
+ `ccmake` instead of `cmake`.
-Author
- Dmitriy Morozov <morozov@cs.duke.edu>
+ When compiling on a 64 bit platform (with Boost 1.34) add
+ `-DBOOST_NO_INTRINSIC_INT64_T` to `CMAKE_CXX_FLAGS`.
+
+## Author
+ Dmitriy Morozov <dmitriy@mrzv.org>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,1 @@
+add_subdirectory (python)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,53 @@
+find_package (PythonLibs)
+link_libraries (${PYTHON_LIBRARIES})
+include_directories (${PYTHON_INCLUDE_PATH})
+link_libraries (${Boost_PYTHON_LIBRARY})
+
+# currently can't build bindings with counters support, eventually FIXME
+remove_definitions (-DCOUNTERS)
+set (sources
+ dionysus.cpp
+ filtration.cpp
+ chain.cpp
+ static-persistence.cpp
+ dynamic-persistence.cpp
+ persistence-diagram.cpp
+ simplex.cpp
+ birthid.cpp
+ zigzag-persistence.cpp
+ cohomology-persistence.cpp
+ rips.cpp
+ distances.cpp
+ )
+set (bindings_libraries ${libraries})
+
+if (CGAL_FOUND)
+ include (${CGAL_USE_FILE})
+
+ set (sources ${sources}
+ alphashapes3d.cpp
+ alphashapes2d.cpp)
+ add_definitions (${CGAL_CXX_FLAGS_INIT})
+ include_directories (${CGAL_INCLUDE_DIRS})
+
+ link_libraries (${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES})
+else (CGAL_FOUND)
+ message(STATUS "CGAL not found, alphashape bindings will not be built")
+ add_definitions (-DNO_CGAL)
+endif (CGAL_FOUND)
+
+add_library (_dionysus SHARED ${sources})
+target_link_libraries (_dionysus ${libraries})
+
+
+# Python files and the symlink
+add_custom_target (dionysus ALL
+ ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/dionysus ${CMAKE_CURRENT_BINARY_DIR}/dionysus
+ DEPENDS dionysus/__init__.py
+ dionysus/distances.py
+ )
+
+get_target_property (_dionysus_location _dionysus LOCATION)
+add_custom_target (dionysus-link ALL
+ ${CMAKE_COMMAND} -E create_symlink ${_dionysus_location} ${CMAKE_CURRENT_BINARY_DIR}/dionysus/_dionysus.so
+ DEPENDS _dionysus)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/alphashapes2d.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,44 @@
+// Wrap includes into namespaces to avoid nameclashes
+#include "../../examples/alphashapes/alphashapes2d.h"
+
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+namespace bp = boost::python;
+
+#include "simplex.h" // defines SimplexVD, Vertex, and Data
+namespace dp = dionysus::python;
+
+
+void fill_alpha2D_complex(bp::object points, bp::object complex)
+{
+ typedef std::map<AlphaSimplex2D::Vertex, unsigned> ASPointMap;
+
+ Delaunay2D Dt;
+ ASPointMap point_map;
+ unsigned i = 0;
+ for (bp::stl_input_iterator<bp::list> pt = points; pt != bp::stl_input_iterator<bp::list>(); ++pt)
+ {
+ double x = bp::extract<double>((*pt)[0]);
+ double y = bp::extract<double>((*pt)[1]);
+ point_map[Dt.insert(Point(x,y))] = i++;
+ }
+
+ AlphaSimplex2D::SimplexSet simplices;
+ fill_simplex_set(Dt, simplices);
+
+ for (AlphaSimplex2D::SimplexSet::const_iterator cur = simplices.begin(); cur != simplices.end(); ++cur)
+ {
+ dp::SimplexVD s;
+ for (AlphaSimplex2D::VertexContainer::const_iterator vcur = cur->vertices().begin();
+ vcur != cur->vertices().end(); ++vcur)
+ s.add(point_map[*vcur]);
+
+ s.data() = bp::object(std::make_pair(cur->value(), !cur->attached())); // regular/critical rather than attached
+ complex.attr("append")(s);
+ }
+}
+
+void export_alphashapes2d()
+{
+ bp::def("fill_alpha2D_complex", &fill_alpha2D_complex);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/alphashapes3d.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,48 @@
+// Wrap includes into namespaces to avoid nameclashes
+#include "../../examples/alphashapes/alphashapes3d.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+namespace bp = boost::python;
+
+#include "utils.h"
+#include "simplex.h" // defines SimplexVD, Vertex, and Data
+namespace dp = dionysus::python;
+
+
+void fill_alpha3D_complex(bp::object points, bp::object complex)
+{
+ typedef std::map<AlphaSimplex3D::Vertex, unsigned> ASPointMap;
+
+ Delaunay3D Dt;
+ ASPointMap point_map;
+ unsigned i = 0;
+ for (bp::stl_input_iterator<bp::list> pt = points; pt != bp::stl_input_iterator<bp::list>(); ++pt)
+ {
+ double x = bp::extract<double>((*pt)[0]);
+ double y = bp::extract<double>((*pt)[1]);
+ double z = bp::extract<double>((*pt)[2]);
+ point_map[Dt.insert(Point(x,y,z))] = i++;
+ }
+
+ AlphaSimplex3D::SimplexSet simplices;
+ fill_simplex_set(Dt, simplices);
+
+ for (AlphaSimplex3D::SimplexSet::const_iterator cur = simplices.begin(); cur != simplices.end(); ++cur)
+ {
+
+ dp::SimplexVD s;
+ for (AlphaSimplex3D::VertexContainer::const_iterator vcur = cur->vertices().begin();
+ vcur != cur->vertices().end(); ++vcur)
+ s.add(point_map[*vcur]);
+
+ s.data() = bp::object(std::make_pair(cur->value(), !cur->attached())); // regular/critical rather than attached
+ complex.attr("append")(s);
+ }
+}
+
+void export_alphashapes3d()
+{
+ bp::def("fill_alpha3D_complex", &fill_alpha3D_complex);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/birthid.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,10 @@
+#include "birthid.h"
+#include "optional.h"
+
+namespace dp = dionysus::python;
+
+void export_birthid()
+{
+ python_optional<dp::BirthID>();
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/birthid.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,15 @@
+#ifndef __PYTHON_BIRTHID_H__
+#define __PYTHON_BIRTHID_H__
+
+#include <boost/python.hpp>
+
+namespace dionysus {
+namespace python {
+
+//typedef int BirthID;
+//typedef boost::python::long_ BirthID;
+typedef boost::python::object BirthID;
+
+} } // namespace dionysus::python
+
+#endif // __PYTHON_BIRTHID_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/chain.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,29 @@
+#include <boost/iterator/indirect_iterator.hpp>
+
+#include <boost/python.hpp>
+#include <boost/python/iterator.hpp>
+#include <boost/python/return_internal_reference.hpp>
+namespace bp = boost::python;
+
+#include "chain.h"
+namespace dp = dionysus::python;
+
+
+template<class Chain>
+boost::indirect_iterator<typename Chain::const_iterator> chain_begin(Chain& c) { return boost::make_indirect_iterator(c.begin()); }
+
+template<class Chain>
+boost::indirect_iterator<typename Chain::const_iterator> chain_end(Chain& c) { return boost::make_indirect_iterator(c.end()); }
+
+void export_chain()
+{
+ bp::class_<dp::VSPChain>("SPChain")
+ .def("__iter__", bp::range<bp::return_internal_reference<1> >(&chain_begin<dp::VSPChain>, &chain_end<dp::VSPChain>))
+ .def("__len__", &dp::VSPChain::size)
+ ;
+
+ bp::class_<dp::VDPChain>("DPChain")
+ .def("__iter__", bp::range<bp::return_internal_reference<1> >(&chain_begin<dp::VDPChain>, &chain_end<dp::VDPChain>))
+ .def("__len__", &dp::VDPChain::size)
+ ;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/chain.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,11 @@
+#include <topology/chain.h>
+#include "static-persistence.h"
+#include "dynamic-persistence.h"
+
+namespace dionysus {
+namespace python {
+
+typedef SPersistence::Chain VSPChain;
+typedef DPersistenceChains::Chain VDPChain;
+
+} } // namespace dionysus::python
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/cohomology-persistence.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,112 @@
+#include <topology/cohomology-persistence.h>
+
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+#include <boost/python/overloads.hpp>
+#include <boost/shared_ptr.hpp>
+namespace bp = boost::python;
+
+#include "cohomology-persistence.h" // defines CohomPersistence
+#include "optional.h"
+namespace dp = dionysus::python;
+
+
+// CohomPersistence
+boost::shared_ptr<dp::CohomPersistence> init_from_prime(unsigned p)
+{
+ dp::CohomPersistence::Field field(p); // Zp
+
+ boost::shared_ptr<dp::CohomPersistence> chp(new dp::CohomPersistence(field));
+ return chp;
+}
+
+boost::shared_ptr<dp::CohomPersistence> init()
+{
+ return init_from_prime(11);
+}
+
+
+bp::tuple chp_add(dp::CohomPersistence& chp,
+ bp::object bdry,
+ dp::BirthID birth,
+ bool store,
+ bool image,
+ bp::object coefficients)
+{
+ dp::CohomPersistence::SimplexIndex i;
+ dp::CohomPersistence::Death d;
+ dp::CohomPersistence::CocyclePtr ccl;
+
+ if (coefficients)
+ {
+ boost::tie(i,d,ccl) = chp.add(bp::stl_input_iterator<int>(coefficients),
+ bp::stl_input_iterator<dp::CohomPersistence::SimplexIndex>(bdry),
+ bp::stl_input_iterator<dp::CohomPersistence::SimplexIndex>(),
+ birth, store, dp::CohomPersistence::SimplexData(), image);
+ } else
+ {
+ boost::tie(i,d,ccl) = chp.add(bp::stl_input_iterator<dp::CohomPersistence::SimplexIndex>(bdry),
+ bp::stl_input_iterator<dp::CohomPersistence::SimplexIndex>(),
+ birth, store, dp::CohomPersistence::SimplexData(), image);
+ }
+
+ return bp::make_tuple(i,d, ccl);
+}
+
+
+dp::CohomPersistence::ZColumn::const_iterator
+zcolumn_begin(dp::CohomPersistence::ZColumn& zcol)
+{ return zcol.begin(); }
+
+dp::CohomPersistence::ZColumn::const_iterator
+zcolumn_end(dp::CohomPersistence::ZColumn& zcol)
+{ return zcol.end(); }
+
+dp::CohomPersistence::ZColumn::const_iterator
+cocycle_zcolumn_begin(dp::CohomPersistence::Cocycle& ccl)
+{ return ccl.zcolumn.begin(); }
+
+dp::CohomPersistence::ZColumn::const_iterator
+cocycle_zcolumn_end(dp::CohomPersistence::Cocycle& ccl)
+{ return ccl.zcolumn.end(); }
+
+// SimplexIndex
+template<class T>
+unsigned si_order(T& si)
+{
+ return si->order;
+}
+
+
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(add_overloads, add, 2, 4)
+
+void export_cohomology_persistence()
+{
+ bp::class_<dp::CohomPersistence::SimplexIndex>("CHSimplexIndex")
+ .add_property("order", &si_order<dp::CohomPersistence::SimplexIndex>)
+ ;
+
+ bp::class_<dp::CohomPersistence::SNode>("CHSNode", bp::no_init)
+ .add_property("coefficient", &dp::CohomPersistence::SNode::coefficient)
+ .add_property("si", &dp::CohomPersistence::SNode::si)
+ ;
+
+ bp::class_<dp::CohomPersistence>("CohomologyPersistence", bp::no_init)
+ .def("__init__", bp::make_constructor(&init))
+ .def("__init__", bp::make_constructor(&init_from_prime))
+ .def("add", &chp_add, (bp::arg("bdry"), bp::arg("birth"), bp::arg("store")=true, bp::arg("image")=true, bp::arg("coefficients")=false))
+
+ .def("__iter__", bp::range(&dp::CohomPersistence::begin, &dp::CohomPersistence::end))
+ .def("show_cocycles", &dp::CohomPersistence::show_cocycles)
+ ;
+
+ bp::class_<dp::CohomPersistence::Cocycle>("Cocycle", bp::no_init)
+ .add_property("birth", &dp::CohomPersistence::Cocycle::birth)
+ .def("__iter__", bp::range(&cocycle_zcolumn_begin, &cocycle_zcolumn_end))
+ ;
+
+ bp::class_<dp::CohomPersistence::ZColumn,
+ boost::shared_ptr<dp::CohomPersistence::ZColumn> >("ZColumn", bp::no_init)
+ .def("__iter__", bp::range(&zcolumn_begin, &zcolumn_end))
+ ;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/cohomology-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,16 @@
+#ifndef __PYTHON_ZIGZAG_PERSISTENCE_H__
+#define __PYTHON_ZIGZAG_PERSISTENCE_H__
+
+#include <topology/cohomology-persistence.h>
+#include <boost/python.hpp>
+
+#include "birthid.h"
+
+namespace dionysus {
+namespace python {
+
+typedef CohomologyPersistence<BirthID> CohomPersistence;
+
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,61 @@
+#include <utilities/log.h>
+#include <boost/python.hpp>
+#include "utils.h"
+
+namespace bp = boost::python;
+namespace dp = dionysus::python;
+
+void export_simplex();
+void export_filtration();
+void export_static_persistence();
+void export_dynamic_persistence_chains();
+void export_chain();
+void export_birthid();
+void export_zigzag_persistence();
+void export_cohomology_persistence();
+void export_point();
+void export_persistence_diagram();
+
+void export_rips();
+void export_pairwise_distances();
+
+#ifndef NO_CGAL
+void export_alphashapes2d();
+void export_alphashapes3d();
+#endif
+
+#ifdef LOGGING
+void enable_log(std::string s)
+{
+ stdoutLog.subscribeTo(RLOG_CHANNEL(s.c_str()));
+}
+#endif
+
+BOOST_PYTHON_MODULE(_dionysus)
+{
+ bp::to_python_converter<std::pair<double, bool>, dp::PairToTupleConverter<double, bool> >();
+
+ export_simplex();
+ export_filtration();
+ export_static_persistence();
+ export_dynamic_persistence_chains();
+ export_chain();
+ export_point();
+ export_persistence_diagram();
+
+ export_birthid();
+ export_zigzag_persistence();
+ export_cohomology_persistence();
+
+ export_rips();
+ export_pairwise_distances();
+
+#ifndef NO_CGAL
+ export_alphashapes2d();
+ export_alphashapes3d();
+#endif
+
+#ifdef LOGGING
+ bp::def("enable_log", &enable_log);
+#endif
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/__init__.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,69 @@
+from _dionysus import *
+from distances import l2, ExplicitDistances, points_file
+from zigzag import *
+from adaptor import *
+import circular
+
+def init_with_none(self, iter, data = None): # convenience: data defaults to None
+ self._cpp_init_(iter, data)
+
+def repr_with_data(self):
+ str = self._cpp_repr_()
+ if type(self.data) == float:
+ str += ' %f' % self.data
+ return str
+
+Simplex._cpp_init_ = Simplex.__init__
+Simplex.__init__ = init_with_none
+Simplex._cpp_repr_ = Simplex.__repr__
+Simplex.__repr__ = repr_with_data
+
+def Simplex_getinitargs(self):
+ return ([v for v in self.vertices], self.data)
+
+Simplex.__getinitargs__ = Simplex_getinitargs
+
+def data_cmp(s1, s2):
+ return cmp(s1.data, s2.data)
+
+def data_dim_cmp(s1,s2):
+ return cmp(s1.dimension(), s2.dimension()) or data_cmp(s1,s2)
+
+def dim_data_cmp(s1,s2):
+ return data_cmp(s1,s2) or cmp(s1.dimension(), s2.dimension())
+
+def vertex_dim_cmp(s1, s2):
+ return cmp(s1.dimension(), s2.dimension()) or vertex_cmp(s1, s2)
+
+def dim_cmp(s1, s2):
+ return cmp(s1.dimension(), s2.dimension())
+
+def fill_alpha_complex(points, simplices):
+ if len(points[0]) == 2: # 2D
+ fill_alpha2D_complex(points, simplices)
+ elif len(points[0]) == 3: # 3D
+ fill_alpha3D_complex(points, simplices)
+
+def closure(simplices, k):
+ """Compute the k-skeleton of the closure of the list of simplices."""
+
+ res = set()
+
+ from itertools import combinations
+ for s in simplices:
+ for kk in xrange(1, k+2):
+ for face in combinations(s.vertices, min(s.dimension() + 1, kk)):
+ res.add(Simplex(face, s.data))
+
+ return list(res)
+
+_init_diagrams = init_diagrams
+
+def init_diagrams(p, f, evaluator = None, data = None):
+ if isinstance(p, StaticCohomologyPersistence):
+ return init_diagrams_from_adaptor(p,f, evaluator, data)
+
+ return _init_diagrams(p,f, evaluator, data)
+
+def read_points(filename):
+ return [p for p in points_file(filename)]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/adaptor.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,114 @@
+from _dionysus import CohomologyPersistence, PersistenceDiagram, Cocycle
+
+class StaticCohomologyPersistence(object):
+ def __init__(self, filtration, prime = 2, subcomplex = lambda s: True):
+ self.filtration = filtration
+ self.prime = prime
+ self.subcomplex = subcomplex
+ self.persistence = CohomologyPersistence(prime)
+ self.pairs = []
+
+ def pair_simplices(self):
+ indices = []
+ for i,s in enumerate(self.filtration):
+ sc = self.subcomplex(s)
+ boundary = (indices[self.filtration(ss)] for ss in s.boundary)
+ idx,d,ccl = self.persistence.add(boundary, i, image = sc)
+ indices.append(idx)
+ self.pairs.append([i, sc, []])
+ if d: # Death
+ if self.pairs[d][1]: # Birth was in the subcomplex
+ self.pairs[i][0] = d # i killed d
+ self.pairs[d][0] = i # d was killed by i
+ self.pairs[d][2] = self._cocycle_list(ccl) # record the cocycle at the time of death
+ else:
+ cocycle = self.persistence.__iter__().next()
+ self.pairs[-1][2] = cocycle
+
+ # Replace cocycles with lists
+ for i in xrange(len(self.pairs)):
+ ccl = self.pairs[i][2]
+ if isinstance(ccl, Cocycle):
+ self.pairs[i][2] = self._cocycle_list(ccl)
+
+ def _cocycle_list(self, ccl):
+ return [(n.coefficient if n.coefficient <= self.prime/2 else n.coefficient - self.prime, n.si.order) for n in ccl]
+
+ def __call__(self, n):
+ return n.i
+
+ def __len__(self):
+ return len(self.pairs)
+
+ def __iter__(self):
+ for i, (pair, subcomplex, cocycle) in enumerate(self.pairs):
+ if pair == i: # unpaired
+ if subcomplex:
+ yield APNode(i, self.pairs)
+ else:
+ if pair > i and subcomplex:
+ yield APNode(i, self.pairs)
+ elif pair < i:
+ pair_pair, pair_subcomplex, pair_cocycle = self.pairs[pair]
+ if pair_subcomplex:
+ yield APNode(i, self.pairs)
+
+ def make_simplex_map(self, filtration):
+ return APSimplexMap(filtration)
+
+class ImagePersistence(StaticCohomologyPersistence):
+ def __init__(self, filtration, subcomplex):
+ super(ImagePersistence, self).__init__(filtration, subcomplex = subcomplex)
+
+# Remaps APNodes into Simplices
+class APSimplexMap:
+ def __init__(self, filtration):
+ self.filtration = filtration
+
+ def __getitem__(self, n):
+ return self.filtration[n.i]
+
+class APNode:
+ def __init__(self, i, pairs):
+ self.i = i
+ self.pairs = pairs
+
+ def sign(self):
+ return self.unpaired() or self.i < self._pair()
+
+ def unpaired(self):
+ return self.i == self._pair()
+
+ def _pair(self):
+ return self.pairs[self.i][0]
+
+ def pair(self):
+ return APNode(self._pair(), self.pairs)
+
+ @property
+ def cocycle(self):
+ return self.pairs[self.i][2]
+
+def init_diagrams_from_adaptor(p, f, evaluator, data):
+ if not evaluator:
+ evaluator = lambda s: s.data
+
+ if not data:
+ data = lambda i: None
+
+ dgms = []
+ smap = p.make_simplex_map(f)
+ for n in p:
+ if not n.sign(): continue
+
+ dim = smap[n].dimension()
+ if dim + 1 > len(dgms):
+ dgms.append(PersistenceDiagram(dim))
+
+ b = evaluator(smap[n])
+ d = evaluator(smap[n.pair()]) if not n.unpaired() else float('inf')
+ if b == d: continue
+
+ dgms[dim].append((b,d, data(n)))
+
+ return dgms
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/circular/__init__.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,66 @@
+def smooth(filtration, cocycle):
+ from cvxopt import spmatrix, matrix
+ from cvxopt.blas import copy
+ from lsqr import lsqr
+
+ coefficient = []
+ coface_indices = []
+ face_indices = []
+ for i,s in enumerate(filtration):
+ if s.dimension() > 2: continue
+
+ c = 1
+ for sb in s.boundary:
+ j = filtration(sb)
+ coefficient.append(c)
+ coface_indices.append(i)
+ face_indices.append(j)
+ c *= -1
+
+ # Cocycle can be larger than D; we implicitly project it down
+ cocycle_max = max(zz[1] for zz in cocycle)
+
+ # D is a coboundary matrix
+ dimension = max(max(coface_indices), max(face_indices), cocycle_max) + 1
+ D = spmatrix(coefficient, coface_indices, face_indices, (dimension, dimension))
+
+ z = spmatrix([zz[0] for zz in cocycle],
+ [zz[1] for zz in cocycle],
+ [0 for zz in cocycle], (dimension, 1))
+
+ v1 = D * z
+ if bool(D*D):
+ raise Exception('D^2 is not 0')
+ if bool(v1):
+ raise Exception('Expected a cocycle as input')
+ z = matrix(z)
+
+ def Dfun(x,y,trans = 'N'):
+ if trans == 'N':
+ copy(D * x, y)
+ elif trans == 'T':
+ copy(D.T * x, y)
+ else:
+ assert False, "Unexpected trans parameter"
+
+ tol = 1e-10
+ show = False
+ maxit = None
+ solution = lsqr(Dfun, matrix(z), show = show, atol = tol, btol = tol, itnlim = maxit)
+
+ z_smooth = z - D*solution[0]
+
+ # print sum(z_smooth**2)
+ # assert sum((D*z_smooth)**2) < tol and sum((D.T*z_smooth)**2) < tol, "Expected a harmonic cocycle"
+ if not (sum((D*z_smooth)**2) < tol and sum((D.T*z_smooth)**2) < tol):
+ raise Exception("Expected a harmonic cocycle: %f %f" % (sum((D*z_smooth)**2), sum((D.T*z_smooth)**2)))
+
+ values = []
+ vertices = ((i,s) for (i,s) in enumerate(filtration) if s.dimension() == 0)
+ for i,s in vertices:
+ v = [v for v in s.vertices][0]
+ if v >= len(values):
+ values.extend((None for i in xrange(len(values), v+1)))
+ values[v] = solution[0][i]
+
+ return values
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/circular/lsqr.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,408 @@
+# LSQR solver from http://pages.cs.wisc.edu/~kline/cvxopt/
+
+from cvxopt import matrix
+from cvxopt.lapack import *
+from cvxopt.blas import *
+from math import sqrt
+
+"""
+a,b are scalars
+
+On exit, returns scalars c,s,r
+"""
+def SymOrtho(a,b):
+ aa=abs(a)
+ ab=abs(b)
+ if b==0.:
+ s=0.
+ r=aa
+ if aa==0.:
+ c=1.
+ else:
+ c=a/aa
+ elif a==0.:
+ c=0.
+ s=b/ab
+ r=ab
+ elif ab>=aa:
+ sb=1
+ if b<0: sb=-1
+ tau=a/b
+ s=sb*(1+tau**2)**-0.5
+ c=s*tau
+ r=b/s
+ elif aa>ab:
+ sa=1
+ if a<0: sa=-1
+ tau=b/a
+ c=sa*(1+tau**2)**-0.5
+ s=c*tau
+ r=a/c
+
+ return c,s,r
+
+"""
+
+It is usually recommended to use SYMMLQ for symmetric matrices
+
+Requires the syntax
+ A(x,y) == y:=[A]*x
+and
+ A(x,y,trans='T') == y:=[A.T]*x
+
+comments with '###' are followed by the intent of the original matlab
+code. This may be useful for debugging.
+
+"""
+
+def lsqr( A, b, damp=0.0, atol=1e-8, btol=1e-8, conlim=1e8, itnlim=None, show=False, wantvar=False):
+ """
+
+ [ x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var ]...
+ = lsqr( m, n, 'aprod', iw, rw, b, damp, atol, btol, conlim, itnlim, show );
+
+ LSQR solves Ax = b or min ||b - Ax||_2 if damp = 0,
+ or min || (b) - ( A )x || otherwise.
+ || (0) (damp I) ||2
+ A is an m by n matrix defined by y = aprod( mode,m,n,x,iw,rw ),
+ where the parameter 'aprodname' refers to a function 'aprod' that
+ performs the matrix-vector operations.
+ If mode = 1, aprod must return y = Ax without altering x.
+ If mode = 2, aprod must return y = A'x without altering x.
+ WARNING: The file containing the function 'aprod'
+ must not be called aprodname.m !!!!
+
+ -----------------------------------------------------------------------
+ LSQR uses an iterative (conjugate-gradient-like) method.
+ For further information, see
+ 1. C. C. Paige and M. A. Saunders (1982a).
+ LSQR: An algorithm for sparse linear equations and sparse least squares,
+ ACM TOMS 8(1), 43-71.
+ 2. C. C. Paige and M. A. Saunders (1982b).
+ Algorithm 583. LSQR: Sparse linear equations and least squares problems,
+ ACM TOMS 8(2), 195-209.
+ 3. M. A. Saunders (1995). Solution of sparse rectangular systems using
+ LSQR and CRAIG, BIT 35, 588-604.
+
+ Input parameters:
+ iw, rw are not used by lsqr, but are passed to aprod.
+ atol, btol are stopping tolerances. If both are 1.0e-9 (say),
+ the final residual norm should be accurate to about 9 digits.
+ (The final x will usually have fewer correct digits,
+ depending on cond(A) and the size of damp.)
+ conlim is also a stopping tolerance. lsqr terminates if an estimate
+ of cond(A) exceeds conlim. For compatible systems Ax = b,
+ conlim could be as large as 1.0e+12 (say). For least-squares
+ problems, conlim should be less than 1.0e+8.
+ Maximum precision can be obtained by setting
+ atol = btol = conlim = zero, but the number of iterations
+ may then be excessive.
+ itnlim is an explicit limit on iterations (for safety).
+ show = 1 gives an iteration log,
+ show = 0 suppresses output.
+
+ Output parameters:
+ x is the final solution.
+ istop gives the reason for termination.
+ istop = 1 means x is an approximate solution to Ax = b.
+ = 2 means x approximately solves the least-squares problem.
+ r1norm = norm(r), where r = b - Ax.
+ r2norm = sqrt( norm(r)^2 + damp^2 * norm(x)^2 )
+ = r1norm if damp = 0.
+ anorm = estimate of Frobenius norm of Abar = [ A ].
+ [damp*I]
+ acond = estimate of cond(Abar).
+ arnorm = estimate of norm(A'*r - damp^2*x).
+ xnorm = norm(x).
+ var (if present) estimates all diagonals of (A'A)^{-1} (if damp=0)
+ or more generally (A'A + damp^2*I)^{-1}.
+ This is well defined if A has full column rank or damp > 0.
+ (Not sure what var means if rank(A) < n and damp = 0.)
+
+
+ 1990: Derived from Fortran 77 version of LSQR.
+ 22 May 1992: bbnorm was used incorrectly. Replaced by anorm.
+ 26 Oct 1992: More input and output parameters added.
+ 01 Sep 1994: Matrix-vector routine is now a parameter 'aprodname'.
+ Print log reformatted.
+ 14 Jun 1997: show added to allow printing or not.
+ 30 Jun 1997: var added as an optional output parameter.
+ 07 Aug 2002: Output parameter rnorm replaced by r1norm and r2norm.
+ Michael Saunders, Systems Optimization Laboratory,
+ Dept of MS&E, Stanford University.
+ -----------------------------------------------------------------------
+ """
+ """
+ Initialize.
+ """
+ n=len(b)
+ m=n
+ if itnlim is None: itnlim=2*n
+
+ msg=('The exact solution is x = 0 ',
+ 'Ax - b is small enough, given atol, btol ',
+ 'The least-squares solution is good enough, given atol ',
+ 'The estimate of cond(Abar) has exceeded conlim ',
+ 'Ax - b is small enough for this machine ',
+ 'The least-squares solution is good enough for this machine',
+ 'Cond(Abar) seems to be too large for this machine ',
+ 'The iteration limit has been reached ');
+
+ var = matrix(0.,(n,1));
+
+ if show:
+ print ' '
+ print 'LSQR Least-squares solution of Ax = b'
+ str1 = 'The matrix A has %8g rows and %8g cols' % (m, n)
+ str2 = 'damp = %20.14e wantvar = %8g' %( damp,wantvar)
+ str3 = 'atol = %8.2e conlim = %8.2e'%( atol, conlim)
+ str4 = 'btol = %8.2e itnlim = %8g' %( btol, itnlim)
+ print str1
+ print str2
+ print str3
+ print str4
+
+ itn = 0; istop = 0; nstop = 0;
+ ctol = 0;
+ if conlim > 0: ctol = 1/conlim
+ anorm = 0; acond = 0;
+ dampsq = damp**2; ddnorm = 0; res2 = 0;
+ xnorm = 0; xxnorm = 0; z = 0;
+ cs2 = -1; sn2 = 0;
+
+ """
+ Set up the first vectors u and v for the bidiagonalization.
+ These satisfy beta*u = b, alfa*v = A'u.
+ """
+ __x = matrix(0., (n,1)) # a matrix for temporary holding
+ v = matrix(0., (n,1))
+ u = +b;
+ x = matrix(0., (n,1))
+ alfa = 0;
+ beta = nrm2( u );
+ w = matrix(0., (n,1))
+
+ if beta > 0:
+ ### u = (1/beta) * u;
+ ### v = feval( aprodname, 2, m, n, u, iw, rw );
+ scal(1/beta,u)
+ A(u,v,trans='T'); #v = feval( aprodname, 2, m, n, u, iw, rw );
+ alfa = nrm2( v );
+
+ if alfa > 0:
+ ### v = (1/alfa) * v;
+ scal(1/alfa,v)
+ copy(v,w)
+
+
+ rhobar = alfa; phibar = beta; bnorm = beta;
+ rnorm = beta;
+ r1norm = rnorm;
+ r2norm = rnorm;
+
+ # reverse the order here from the original matlab code because
+ # there was an error on return when arnorm==0
+ arnorm = alfa * beta;
+ if arnorm == 0:
+ print msg[0];
+ return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var
+
+ head1 = ' Itn x[0] r1norm r2norm ';
+ head2 = ' Compatible LS Norm A Cond A';
+
+ if show:
+ print ' '
+ print head1, head2
+ test1 = 1; test2 = alfa / beta;
+ str1 = '%6g %12.5e' %( itn, x[0] );
+ str2 = ' %10.3e %10.3e'%( r1norm, r2norm );
+ str3 = ' %8.1e %8.1e' %( test1, test2 );
+ print str1, str2, str3
+
+ """
+ %------------------------------------------------------------------
+ % Main iteration loop.
+ %------------------------------------------------------------------
+ """
+ while itn < itnlim:
+ itn = itn + 1;
+ """
+ % Perform the next step of the bidiagonalization to obtain the
+ % next beta, u, alfa, v. These satisfy the relations
+ % beta*u = a*v - alfa*u,
+ % alfa*v = A'*u - beta*v.
+ """
+ ### u = feval( aprodname, 1, m, n, v, iw, rw ) - alfa*u;
+ copy(u, __x)
+ A(v,u)
+ axpy(__x,u,-alfa)
+
+ beta = nrm2( u );
+ if beta > 0:
+ ### u = (1/beta) * u;
+ scal(1/beta,u)
+ anorm = sqrt(anorm**2 + alfa**2 + beta**2 + damp**2);
+ ### v = feval( aprodname, 2, m, n, u, iw, rw ) - beta*v;
+ copy(v,__x)
+ A(u,v,trans='T')
+ axpy(__x,v,-beta)
+
+ alfa = nrm2( v );
+ if alfa > 0:
+ ### v = (1/alfa) * v;
+ scal(1/alfa, v)
+
+ """
+ % Use a plane rotation to eliminate the damping parameter.
+ % This alters the diagonal (rhobar) of the lower-bidiagonal matrix.
+ """
+
+ rhobar1 = sqrt(rhobar**2 + damp**2);
+ cs1 = rhobar / rhobar1;
+ sn1 = damp / rhobar1;
+ psi = sn1 * phibar;
+ phibar = cs1 * phibar;
+ """
+ % Use a plane rotation to eliminate the subdiagonal element (beta)
+ % of the lower-bidiagonal matrix, giving an upper-bidiagonal matrix.
+ """
+
+
+ ###cs = rhobar1/ rho;
+ ###sn = beta / rho;
+ cs,sn,rho = SymOrtho(rhobar1,beta)
+
+ theta = sn * alfa;
+ rhobar = - cs * alfa;
+ phi = cs * phibar;
+ phibar = sn * phibar;
+ tau = sn * phi;
+ """
+ % Update x and w.
+ """
+ t1 = phi /rho;
+ t2 = - theta/rho;
+ dk = (1/rho)*w;
+
+ ### x = x + t1*w;
+ axpy(w,x,t1)
+ ### w = v + t2*w;
+ scal(t2,w)
+ axpy(v,w)
+ ddnorm = ddnorm + nrm2(dk)**2;
+ if wantvar:
+ ### var = var + dk.*dk;
+ axpy(dk**2, var)
+ """
+ % Use a plane rotation on the right to eliminate the
+ % super-diagonal element (theta) of the upper-bidiagonal matrix.
+ % Then use the result to estimate norm(x).
+ """
+
+ delta = sn2 * rho;
+ gambar = - cs2 * rho;
+ rhs = phi - delta * z;
+ zbar = rhs / gambar;
+ xnorm = sqrt(xxnorm + zbar**2);
+ gamma = sqrt(gambar**2 +theta**2);
+ cs2 = gambar / gamma;
+ sn2 = theta / gamma;
+ z = rhs / gamma;
+ xxnorm = xxnorm + z**2;
+ """
+ % Test for convergence.
+ % First, estimate the condition of the matrix Abar,
+ % and the norms of rbar and Abar'rbar.
+ """
+ acond = anorm * sqrt(ddnorm);
+ res1 = phibar**2;
+ res2 = res2 + psi**2;
+ rnorm = sqrt( res1 + res2 );
+ arnorm = alfa * abs( tau );
+ """
+ % 07 Aug 2002:
+ % Distinguish between
+ % r1norm = ||b - Ax|| and
+ % r2norm = rnorm in current code
+ % = sqrt(r1norm^2 + damp^2*||x||^2).
+ % Estimate r1norm from
+ % r1norm = sqrt(r2norm^2 - damp^2*||x||^2).
+ % Although there is cancellation, it might be accurate enough.
+ """
+ r1sq = rnorm**2 - dampsq * xxnorm;
+ r1norm = sqrt( abs(r1sq) );
+ if r1sq < 0: r1norm = - r1norm;
+ r2norm = rnorm;
+ """
+ % Now use these norms to estimate certain other quantities,
+ % some of which will be small near a solution.
+ """
+ test1 = rnorm / bnorm;
+ test2 = arnorm/( anorm * rnorm );
+ test3 = 1 / acond;
+ t1 = test1 / (1 + anorm * xnorm / bnorm);
+ rtol = btol + atol * anorm * xnorm / bnorm;
+ """
+ % The following tests guard against extremely small values of
+ % atol, btol or ctol. (The user may have set any or all of
+ % the parameters atol, btol, conlim to 0.)
+ % The effect is equivalent to the normal tests using
+ % atol = eps, btol = eps, conlim = 1/eps.
+ """
+ if itn >= itnlim : istop = 7;
+ if 1 + test3 <= 1: istop = 6;
+ if 1 + test2 <= 1: istop = 5;
+ if 1 + t1 <= 1: istop = 4;
+ """
+ % Allow for tolerances set by the user.
+ """
+ if test3 <= ctol: istop = 3;
+ if test2 <= atol: istop = 2;
+ if test1 <= rtol: istop = 1;
+ """
+ % See if it is time to print something.
+ """
+ prnt = False;
+ if n <= 40 : prnt = True;
+ if itn <= 10 : prnt = True;
+ if itn >= itnlim-10: prnt = True;
+ # if itn%10 == 0 : prnt = True;
+ if test3 <= 2*ctol : prnt = True;
+ if test2 <= 10*atol : prnt = True;
+ if test1 <= 10*rtol : prnt = True;
+ if istop != 0 : prnt = True;
+
+ if prnt:
+ if show:
+ str1 = '%6g %12.5e'% (itn, x[0] );
+ str2 = ' %10.3e %10.3e'% (r1norm, r2norm );
+ str3 = ' %8.1e %8.1e'% ( test1, test2 );
+ str4 = ' %8.1e %8.1e'% ( anorm, acond );
+ print str1, str2, str3, str4
+
+ if istop != 0: break
+
+ """
+ % End of iteration loop.
+ % Print the stopping condition.
+ """
+ if show:
+ print ' '
+ print 'LSQR finished'
+ print msg[istop]
+ print ' '
+ str1 = 'istop =%8g r1norm =%8.1e'% ( istop, r1norm );
+ str2 = 'anorm =%8.1e arnorm =%8.1e'%( anorm, arnorm );
+ str3 = 'itn =%8g r2norm =%8.1e'% ( itn, r2norm );
+ str4 = 'acond =%8.1e xnorm =%8.1e'%( acond, xnorm );
+ print str1+ ' ' +str2
+ print str3+ ' ' +str4
+ print ' '
+
+ return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var
+
+ """
+ %-----------------------------------------------------------------------
+ % End of lsqr.m
+ %-----------------------------------------------------------------------
+ """
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/distances.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,40 @@
+from math import sqrt
+
+def l2(p):
+ return sqrt(sum((x**2 for x in p)))
+
+# Pairwise distances between the elements of `points` with respect to some `norm`
+class PairwiseDistances:
+ def __init__(self, points, norm = l2):
+ self.points = points
+ self.norm = norm
+
+ def __len__(self):
+ return len(self.points)
+
+ def __call__(self, p1, p2):
+ return self.norm((x - y for (x,y) in zip(self.points[p1], self.points[p2])))
+
+# Caches all distances specified by `distances`
+class ExplicitDistances:
+ def __init__(self, distances):
+ self.len = len(distances)
+ self.distances = []
+ for i in xrange(self.len):
+ self.distances.append([])
+ for j in xrange(self.len):
+ self.distances[-1].append(distances(i,j))
+
+ def __len__(self):
+ return self.len
+
+ def __call__(self, p1, p2):
+ return self.distances[p1][p2]
+
+# Generator of all points in a file `filename` with one point per line
+def points_file(filename):
+ fd = open(filename)
+ for line in fd.xreadlines():
+ if line.startswith('#'): continue
+ yield map(float, line.strip().split())
+ fd.close()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/viewer/PyGLWidget.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,309 @@
+# -*- coding: utf-8 -*-
+#===============================================================================
+#
+# PyGLWidget.py
+#
+# A simple GL Viewer.
+#
+# Copyright (c) 2011, Arne Schmitz <arne.schmitz@gmx.net>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the <organization> nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#===============================================================================
+
+from PyQt4 import QtCore, QtGui, QtOpenGL
+import math
+import numpy
+import numpy.linalg as linalg
+import OpenGL
+OpenGL.ERROR_CHECKING = False
+from OpenGL.GL import *
+from OpenGL.GLU import *
+
+class PyGLWidget(QtOpenGL.QGLWidget):
+
+ # Qt signals
+ signalGLMatrixChanged = QtCore.pyqtSignal()
+ rotationBeginEvent = QtCore.pyqtSignal()
+ rotationEndEvent = QtCore.pyqtSignal()
+
+ def __init__(self, parent = None):
+ format = QtOpenGL.QGLFormat()
+ format.setSampleBuffers(True)
+ QtOpenGL.QGLWidget.__init__(self, format, parent)
+ self.setCursor(QtCore.Qt.OpenHandCursor)
+ self.setMouseTracking(True)
+
+ self.modelview_matrix_ = []
+ self.translate_vector_ = [0.0, 0.0, 0.0]
+ self.viewport_matrix_ = []
+ self.projection_matrix_ = []
+ self.near_ = 0.1
+ self.far_ = 100.0
+ self.fovy_ = 45.0
+ self.radius_ = 5.0
+ self.last_point_2D_ = QtCore.QPoint()
+ self.last_point_ok_ = False
+ self.last_point_3D_ = [1.0, 0.0, 0.0]
+ self.isInRotation_ = False
+
+ # connections
+ #self.signalGLMatrixChanged.connect(self.printModelViewMatrix)
+
+ @QtCore.pyqtSlot()
+ def printModelViewMatrix(self):
+ print self.modelview_matrix_
+
+ def initializeGL(self):
+ # OpenGL state
+ glClearColor(1.0, 1.0, 1.0, 0.0)
+ glEnable(GL_DEPTH_TEST)
+ self.reset_view()
+
+ def resizeGL(self, width, height):
+ glViewport( 0, 0, width, height );
+ self.set_projection( self.near_, self.far_, self.fovy_ );
+ self.updateGL()
+
+ def paintGL(self):
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+ glMatrixMode(GL_MODELVIEW)
+ glLoadMatrixd(self.modelview_matrix_)
+
+ def set_projection(self, _near, _far, _fovy):
+ self.near_ = _near
+ self.far_ = _far
+ self.fovy_ = _fovy
+ self.makeCurrent()
+ glMatrixMode( GL_PROJECTION )
+ glLoadIdentity()
+ gluPerspective( self.fovy_, float(self.width()) / float(self.height()),
+ self.near_, self.far_ )
+ self.updateGL()
+
+ def set_center(self, _cog):
+ self.center_ = _cog
+ self.view_all()
+
+ def set_radius(self, _radius):
+ self.radius_ = _radius
+ self.set_projection(_radius / 100.0, _radius * 100.0, self.fovy_)
+ self.reset_view()
+ self.translate([0, 0, -_radius * 2.0])
+ self.view_all()
+ self.updateGL()
+
+ def reset_view(self):
+ # scene pos and size
+ glMatrixMode( GL_MODELVIEW )
+ glLoadIdentity();
+ self.modelview_matrix_ = glGetDoublev( GL_MODELVIEW_MATRIX )
+ self.set_center([0.0, 0.0, 0.0])
+
+ def reset_rotation(self):
+ self.modelview_matrix_[0] = [1.0, 0.0, 0.0, 0.0]
+ self.modelview_matrix_[1] = [0.0, 1.0, 0.0, 0.0]
+ self.modelview_matrix_[2] = [0.0, 0.0, 1.0, 0.0]
+ glMatrixMode(GL_MODELVIEW)
+ glLoadMatrixd(self.modelview_matrix_)
+ self.updateGL()
+
+ def translate(self, _trans):
+ # Translate the object by _trans
+ # Update modelview_matrix_
+ self.makeCurrent()
+ glMatrixMode(GL_MODELVIEW)
+ glLoadIdentity()
+ glTranslated(_trans[0], _trans[1], _trans[2])
+ glMultMatrixd(self.modelview_matrix_)
+ self.modelview_matrix_ = glGetDoublev(GL_MODELVIEW_MATRIX)
+ self.translate_vector_[0] = self.modelview_matrix_[3][0]
+ self.translate_vector_[1] = self.modelview_matrix_[3][1]
+ self.translate_vector_[2] = self.modelview_matrix_[3][2]
+ self.signalGLMatrixChanged.emit()
+
+ def rotate(self, _axis, _angle):
+ t = [self.modelview_matrix_[0][0] * self.center_[0] +
+ self.modelview_matrix_[1][0] * self.center_[1] +
+ self.modelview_matrix_[2][0] * self.center_[2] +
+ self.modelview_matrix_[3][0],
+ self.modelview_matrix_[0][1] * self.center_[0] +
+ self.modelview_matrix_[1][1] * self.center_[1] +
+ self.modelview_matrix_[2][1] * self.center_[2] +
+ self.modelview_matrix_[3][1],
+ self.modelview_matrix_[0][2] * self.center_[0] +
+ self.modelview_matrix_[1][2] * self.center_[1] +
+ self.modelview_matrix_[2][2] * self.center_[2] +
+ self.modelview_matrix_[3][2]]
+
+ self.makeCurrent()
+ glLoadIdentity()
+ glTranslatef(t[0], t[1], t[2])
+ glRotated(_angle, _axis[0], _axis[1], _axis[2])
+ glTranslatef(-t[0], -t[1], -t[2])
+ glMultMatrixd(self.modelview_matrix_)
+ self.modelview_matrix_ = glGetDoublev(GL_MODELVIEW_MATRIX)
+ self.signalGLMatrixChanged.emit()
+
+ def view_all(self):
+ self.translate( [ -( self.modelview_matrix_[0][0] * self.center_[0] +
+ self.modelview_matrix_[0][1] * self.center_[1] +
+ self.modelview_matrix_[0][2] * self.center_[2] +
+ self.modelview_matrix_[0][3]),
+ -( self.modelview_matrix_[1][0] * self.center_[0] +
+ self.modelview_matrix_[1][1] * self.center_[1] +
+ self.modelview_matrix_[1][2] * self.center_[2] +
+ self.modelview_matrix_[1][3]),
+ -( self.modelview_matrix_[2][0] * self.center_[0] +
+ self.modelview_matrix_[2][1] * self.center_[1] +
+ self.modelview_matrix_[2][2] * self.center_[2] +
+ self.modelview_matrix_[2][3] +
+ self.radius_ / 2.0 )])
+
+ def map_to_sphere(self, _v2D):
+ _v3D = [0.0, 0.0, 0.0]
+ # inside Widget?
+ if (( _v2D.x() >= 0 ) and ( _v2D.x() <= self.width() ) and
+ ( _v2D.y() >= 0 ) and ( _v2D.y() <= self.height() ) ):
+ # map Qt Coordinates to the centered unit square [-0.5..0.5]x[-0.5..0.5]
+ x = float( _v2D.x() - 0.5 * self.width()) / self.width()
+ y = float( 0.5 * self.height() - _v2D.y()) / self.height()
+
+ _v3D[0] = x;
+ _v3D[1] = y;
+ # use Pythagoras to comp z-coord (the sphere has radius sqrt(2.0*0.5*0.5))
+ z2 = 2.0*0.5*0.5-x*x-y*y;
+ # numerical robust sqrt
+ _v3D[2] = math.sqrt(max( z2, 0.0 ))
+
+ # normalize direction to unit sphere
+ n = linalg.norm(_v3D)
+ _v3D = numpy.array(_v3D) / n
+
+ return True, _v3D
+ else:
+ return False, _v3D
+
+ def wheelEvent(self, _event):
+ # Use the mouse wheel to zoom in/out
+
+ d = - float(_event.delta()) / 200.0 * self.radius_
+ self.translate([0.0, 0.0, d])
+ self.updateGL()
+ _event.accept()
+
+ def mousePressEvent(self, _event):
+ self.last_point_2D_ = _event.pos()
+ self.last_point_ok_, self.last_point_3D_ = self.map_to_sphere(self.last_point_2D_)
+
+ def mouseMoveEvent(self, _event):
+ newPoint2D = _event.pos()
+
+ if ((newPoint2D.x() < 0) or (newPoint2D.x() > self.width()) or
+ (newPoint2D.y() < 0) or (newPoint2D.y() > self.height())):
+ return
+
+ # Left button: rotate around center_
+ # Middle button: translate object
+ # Left & middle button: zoom in/out
+
+ value_y = 0
+ newPoint_hitSphere, newPoint3D = self.map_to_sphere(newPoint2D)
+
+ dx = float(newPoint2D.x() - self.last_point_2D_.x())
+ dy = float(newPoint2D.y() - self.last_point_2D_.y())
+
+ w = float(self.width())
+ h = float(self.height())
+
+ # enable GL context
+ self.makeCurrent()
+
+ # move in z direction
+ if (((_event.buttons() & QtCore.Qt.LeftButton) and (_event.buttons() & QtCore.Qt.MidButton))
+ or (_event.buttons() & QtCore.Qt.LeftButton and _event.modifiers() & QtCore.Qt.ControlModifier)):
+ value_y = self.radius_ * dy * 2.0 / h;
+ self.translate([0.0, 0.0, value_y])
+ # move in x,y direction
+ elif (_event.buttons() & QtCore.Qt.MidButton
+ or (_event.buttons() & QtCore.Qt.LeftButton and _event.modifiers() & QtCore.Qt.ShiftModifier)):
+ z = - (self.modelview_matrix_[0][2] * self.center_[0] +
+ self.modelview_matrix_[1][2] * self.center_[1] +
+ self.modelview_matrix_[2][2] * self.center_[2] +
+ self.modelview_matrix_[3][2]) / (self.modelview_matrix_[0][3] * self.center_[0] +
+ self.modelview_matrix_[1][3] * self.center_[1] +
+ self.modelview_matrix_[2][3] * self.center_[2] +
+ self.modelview_matrix_[3][3])
+
+ fovy = 45.0
+ aspect = w / h
+ n = 0.01 * self.radius_
+ up = math.tan(fovy / 2.0 * math.pi / 180.0) * n
+ right = aspect * up
+
+ self.translate( [2.0 * dx / w * right / n * z,
+ -2.0 * dy / h * up / n * z,
+ 0.0] )
+
+
+ # rotate
+ elif (_event.buttons() & QtCore.Qt.LeftButton):
+ if (not self.isInRotation_):
+ self.isInRotation_ = True
+ self.rotationBeginEvent.emit()
+
+ axis = [0.0, 0.0, 0.0]
+ angle = 0.0
+
+ if (self.last_point_ok_ and newPoint_hitSphere):
+ axis = numpy.cross(self.last_point_3D_, newPoint3D)
+ cos_angle = numpy.dot(self.last_point_3D_, newPoint3D)
+ if (abs(cos_angle) < 1.0):
+ angle = math.acos(cos_angle) * 180.0 / math.pi
+ angle *= 2.0
+ self.rotate(axis, angle)
+
+ # remember this point
+ self.last_point_2D_ = newPoint2D
+ self.last_point_3D_ = newPoint3D
+ self.last_point_ok_ = newPoint_hitSphere
+
+ # trigger redraw
+ self.updateGL()
+
+ def mouseReleaseEvent(self, _event):
+ if (isInRotation_):
+ isInRotation_ = false
+ self.rotationEndEvent.emit()
+ last_point_ok_ = False
+
+#===============================================================================
+#
+# Local Variables:
+# mode: Python
+# indent-tabs-mode: nil
+# End:
+#
+#===============================================================================
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/viewer/__init__.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,16 @@
+from diagram import show_diagram as _show_diagram
+from complex2d import show_complex_2D as _show_complex_2D
+from complex3d import show_complex_3D as _show_complex_3D
+
+from PyQt4 import QtGui
+
+_app = QtGui.QApplication([])
+
+def show_complex(points, complex = None, values = None, subcomplex = None, **kwargs):
+ if len(points[0]) == 2:
+ _show_complex_2D(points, complex, values, subcomplex, app = _app, **kwargs)
+ if len(points[0]) == 3:
+ _show_complex_3D(points, complex, values, subcomplex, app = _app, **kwargs)
+
+def show_diagram(dgm, noise = 0):
+ return _show_diagram(dgm, noise, _app)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/viewer/complex2d.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,117 @@
+from PyQt4 import QtGui, QtCore
+from dionysus import Simplex
+
+class ComplexViewer2D(QtGui.QGraphicsView):
+ def __init__(self, points, complex = None, values = None, subcomplex = None):
+ super(QtGui.QGraphicsView, self).__init__()
+ self._pan = False
+
+ self.points = points
+ if complex:
+ complex = [s for s in complex]
+ else:
+ # Create vertex simplices if no complex provided
+ complex = [Simplex([i]) for i in xrange(len(self.points))]
+
+ if not subcomplex:
+ subcomplex = []
+
+ if not values:
+ values = [0]*len(self.points)
+ self.values = values
+ self.maxval, self.minval = max(values), min(values)
+
+ self.setRenderHint(QtGui.QPainter.Antialiasing)
+ self.scene = QtGui.QGraphicsScene(self)
+ self.setScene(self.scene)
+
+ minx = min(p[0] for p in points)
+ miny = min(p[1] for p in points)
+ maxx = max(p[0] for p in points)
+ maxy = max(p[1] for p in points)
+
+ radius = min(maxx - minx, maxy - miny)/100
+ self.scene.setSceneRect(minx - 10*radius, miny - 10*radius, (maxx - minx) + 20*radius, (maxy - miny) + 20*radius)
+
+ self.draw_complex(complex, radius, colormap = self.colormap)
+ self.draw_complex(subcomplex, 3*radius, colormap = lambda v: QtCore.Qt.green, line_color = QtCore.Qt.green)
+
+ # Flip y-axis
+ self.scale(1,-1)
+
+ # Set the correct view
+ rect = self.scene.itemsBoundingRect()
+ self.fitInView(rect, QtCore.Qt.KeepAspectRatio)
+
+ def draw_complex(self, complex, radius, colormap, line_color = QtCore.Qt.black):
+ complex.sort(lambda s1, s2: -cmp(s1.dimension(), s2.dimension()))
+ for s in complex:
+ vertices = [v for v in s.vertices]
+ if s.dimension() == 0: # point
+ p = self.points[vertices[0]]
+ v = self.values[vertices[0]]
+ item = QtGui.QGraphicsEllipseItem(p[0] - radius/2,p[1] - radius/2,radius,radius)
+ color = colormap(v)
+ item.setBrush(QtGui.QBrush(color))
+ item.setPen(QtGui.QPen(color))
+ elif s.dimension() == 1: # edge
+ p0 = self.points[vertices[0]]
+ p1 = self.points[vertices[1]]
+ item = QtGui.QGraphicsLineItem(p0[0], p0[1], p1[0], p1[1])
+ item.setPen(QtGui.QPen(line_color))
+ else: # higher-d simplex
+ pts = [QtCore.QPointF(self.points[v][0], self.points[v][1]) for v in vertices]
+ item = QtGui.QGraphicsPolygonItem(QtGui.QPolygonF(pts))
+ item.setBrush(QtCore.Qt.blue)
+
+ self.scene.addItem(item)
+
+ def colormap(self, v):
+ if self.maxval <= self.minval:
+ t = 0
+ else:
+ t = (v - self.minval)/(self.maxval - self.minval)
+ c = QtGui.QColor()
+ c.setHsv(int(t*255), 255, 255)
+ return c
+
+ def wheelEvent(self, event):
+ delta = 1 + float(event.delta())/100
+ if delta < 0:
+ event.ignore()
+ return
+ self.scale(delta, delta)
+ event.accept()
+
+ def mousePressEvent(self, event):
+ if event.button() == QtCore.Qt.RightButton:
+ self._pan = True
+ self._panStartX = event.x()
+ self._panStartY = event.y()
+ self.setCursor(QtCore.Qt.ClosedHandCursor)
+ event.accept()
+
+ def mouseReleaseEvent(self, event):
+ if event.button() == QtCore.Qt.RightButton:
+ self._pan = False
+ self.setCursor(QtCore.Qt.ArrowCursor)
+ event.accept()
+ return
+ event.ignore()
+
+ def mouseMoveEvent(self, event):
+ if self._pan:
+ self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - (event.x() - self._panStartX))
+ self.verticalScrollBar().setValue(self.verticalScrollBar().value() - (event.y() - self._panStartY))
+ self._panStartX = event.x()
+ self._panStartY = event.y()
+ event.accept()
+ return
+ event.ignore()
+
+def show_complex_2D(points, complex = None, values = None, subcomplex = None, app = None):
+ #app = QtGui.QApplication([])
+ view = ComplexViewer2D(points, complex, values, subcomplex)
+ view.show()
+ view.raise_()
+ app.exec_()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/viewer/complex3d.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,118 @@
+from PyQt4 import QtGui, QtCore
+from PyGLWidget import PyGLWidget
+from OpenGL.GL import *
+from dionysus import Simplex
+from math import sqrt
+
+class ComplexViewer3D(PyGLWidget):
+ def __init__(self, points, complex = None, values = None, subcomplex = None, point_size = 3.):
+ self.display_list = None
+ PyGLWidget.__init__(self)
+
+ #glEnable( GL_BLEND )
+ #glEnable( GL_LINE_SMOOTH )
+ #glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
+
+ self.point_size = point_size
+ self.points = points
+ if complex:
+ self.complex = [s for s in complex]
+ else:
+ # Create vertex simplices if no complex provided
+ self.complex = [Simplex([i]) for i in xrange(len(self.points))]
+
+ if subcomplex:
+ self.subcomplex = subcomplex
+ else:
+ self.subcomplex = []
+
+ self.values = values
+ if not values:
+ self.values = [0]*len(self.points)
+ self.maxval, self.minval = max(self.values), min(self.values)
+
+ center, radius = self.center_radius()
+ self.set_radius(radius)
+ self.set_center(center)
+
+ self.make_display_list()
+
+ def center_radius(self):
+ c = [0,0,0]
+ for p in self.points:
+ for i in xrange(3): c[i] += p[i]
+ for i in xrange(3): c[i] /= len(self.points)
+
+ r = 0
+ for p in self.points:
+ d = sqrt((p[0] - c[0])**2 + (p[1] - c[1])**2 + (p[2] - c[2])**2)
+ if d > r: r = d
+ return c,r
+
+ def paintGL(self):
+ PyGLWidget.paintGL(self)
+ if self.display_list:
+ glCallList(self.display_list)
+
+ def make_display_list(self):
+ self.display_list = glGenLists(1)
+ glNewList(self.display_list, GL_COMPILE)
+ self.draw_complex(self.complex, self.point_size, 2., self.colormap)
+ self.draw_complex(self.subcomplex, 2*self.point_size, 4., colormap = lambda v: (0,1.,0), line_color = (0,1.,0))
+ glEndList()
+
+
+ def draw_complex(self, complex, point_size, line_size, colormap, line_color = (0,0,1.)):
+ glPointSize(point_size)
+ glLineWidth(line_size)
+ complex.sort(lambda s1, s2: -cmp(s1.dimension(), s2.dimension()))
+ for s in complex:
+ vertices = [v for v in s.vertices]
+ if s.dimension() == 0: # point
+ p = self.points[vertices[0]]
+ v = self.values[vertices[0]]
+
+ c = self.colormap(v)
+ glColor3f(*c)
+ glBegin(GL_POINTS)
+ glVertex3f(p[0],p[1],p[2])
+ glEnd()
+ if s.dimension() == 1: # edge
+ p0 = self.points[vertices[0]]
+ p1 = self.points[vertices[1]]
+
+ glColor3f(*line_color)
+ glBegin(GL_LINES)
+ glVertex3f(p0[0],p0[1],p0[2])
+ glVertex3f(p1[0],p1[1],p1[2])
+ glEnd()
+ elif s.dimension() == 2:
+ p0 = self.points[vertices[0]]
+ p1 = self.points[vertices[1]]
+ p2 = self.points[vertices[2]]
+
+ glColor3f(1,1,0)
+ glBegin(GL_TRIANGLES)
+ glVertex3f(p0[0],p0[1],p0[2])
+ glVertex3f(p1[0],p1[1],p1[2])
+ glVertex3f(p2[0],p2[1],p2[2])
+ glEnd()
+
+ def colormap(self, v):
+ if self.maxval <= self.minval:
+ t = 0
+ else:
+ t = (v - self.minval)/(self.maxval - self.minval)
+ c = QtGui.QColor()
+ c.setHsv(int(t*255), 255, 255)
+ cr = float(c.red())/255
+ cg = float(c.green())/255
+ cb = float(c.blue())/255
+ return (cr,cg,cb)
+
+def show_complex_3D(points, complex = None, values = None, subcomplex = None, app = None, point_size = 3.):
+ #app = QtGui.QApplication([])
+ view = ComplexViewer3D(points, complex, values, subcomplex, point_size)
+ view.show()
+ view.raise_()
+ app.exec_()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/viewer/diagram.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,156 @@
+from PyQt4 import QtGui, QtCore
+from math import fabs
+
+class DiagramPoint(QtGui.QGraphicsEllipseItem):
+ def __init__(self,x,y, p, infty = False, color = 0):
+ super(QtGui.QGraphicsEllipseItem, self).__init__()
+ c = self.color(color)
+ self.setBrush(QtGui.QBrush(c[0]))
+ self.setPen(QtGui.QPen(c[1]))
+ self.radius = .075
+ if infty:
+ self.radius *= 2
+ self.x, self.y = x,y
+ self.scale(1)
+ self.p = p
+
+ def scale(self, delta):
+ self.radius *= delta
+ self.setRect(self.x - self.radius, self.y - self.radius, 2*self.radius, 2*self.radius)
+
+ def color(self, i):
+ return self._colors[i % len(self._colors)]
+
+ # (fill, border) pairs
+ _colors = [(QtCore.Qt.red, QtGui.QColor(225, 0, 0)),
+ (QtCore.Qt.blue, QtGui.QColor(0, 0, 225)),
+ (QtCore.Qt.green, QtGui.QColor(0, 225, 0)),
+ ]
+
+class DiagramViewer(QtGui.QGraphicsView):
+ def __init__(self, dgm, noise):
+ super(QtGui.QGraphicsView, self).__init__()
+
+ self.selection = None
+ self._pan = False
+
+ self.setRenderHint(QtGui.QPainter.Antialiasing)
+ self.scene = QtGui.QGraphicsScene(self)
+ self.setScene(self.scene)
+
+ if not isinstance(dgm, list):
+ # Assume it's just a single diagram
+ dgms = [dgm]
+ else:
+ dgms = dgm
+
+ inf = float('inf')
+ xs = [p[0] for d in dgms for p in d]
+ ys = [p[1] for d in dgms for p in d]
+ minx = min(0, min(xs) if xs else 0)
+ miny = min(0, min(ys) if ys else 0)
+ xs = [x for x in xs if x != inf]
+ ys = [y for y in ys if y != inf]
+ maxx = max(0, max(xs) if xs else 0)
+ maxy = max(0, max(ys) if ys else 0)
+
+ self.draw_axes(minx,miny,maxx,maxy)
+
+ for i, dgm in enumerate(dgms):
+ for p in dgm:
+ x,y = p[0],p[1]
+ if fabs(y - x) < noise:
+ continue
+ if fabs(x) == inf or fabs(y) == inf:
+ if x == inf: x = maxx + 2
+ if y == inf: y = maxy + 2
+ if x == -inf: x = minx - 2
+ if y == -inf: y = miny - 2
+ item = DiagramPoint(x,y,p, infty = True, color = i)
+ else:
+ item = DiagramPoint(x,y,p, color = i)
+ self.scene.addItem(item)
+
+ # Flip y-axis
+ self.scale(1, -1)
+
+ # Set the correct view
+ rect = self.scene.itemsBoundingRect()
+ self.fitInView(rect, QtCore.Qt.KeepAspectRatio)
+
+ def mousePressEvent(self, event):
+ if event.button() == QtCore.Qt.RightButton:
+ self._pan = True
+ self._panStartX = event.x()
+ self._panStartY = event.y()
+ self.setCursor(QtCore.Qt.ClosedHandCursor)
+ event.accept()
+ else:
+ p = self.mapToScene(event.pos())
+ item = self.scene.itemAt(p)
+ if isinstance(item, DiagramPoint):
+ self.selection = item.p
+ self.close()
+
+ def mouseReleaseEvent(self, event):
+ if event.button() == QtCore.Qt.RightButton:
+ self._pan = False
+ self.setCursor(QtCore.Qt.ArrowCursor)
+ event.accept()
+ return
+ event.ignore()
+
+ def mouseMoveEvent(self, event):
+ if self._pan:
+ self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - (event.x() - self._panStartX))
+ self.verticalScrollBar().setValue(self.verticalScrollBar().value() - (event.y() - self._panStartY))
+ self._panStartX = event.x()
+ self._panStartY = event.y()
+ event.accept()
+ return
+ event.ignore()
+
+ def wheelEvent(self, event):
+ delta = 1 + float(event.delta())/100
+ if delta < 0:
+ event.ignore()
+ return
+ self.scale(delta, delta)
+ for item in self.scene.items():
+ if isinstance(item, DiagramPoint):
+ item.scale(1/delta)
+ event.accept()
+
+ def draw_axes(self, minx, miny, maxx, maxy):
+ # Draw axes and diagonal
+ if maxx > 0:
+ self.scene.addItem(QtGui.QGraphicsLineItem(0,0, maxx, 0))
+ if minx < 0:
+ self.scene.addItem(QtGui.QGraphicsLineItem(minx,0, 0, 0))
+ if maxy > 0:
+ self.scene.addItem(QtGui.QGraphicsLineItem(0,0, 0, maxy))
+ if miny < 0:
+ self.scene.addItem(QtGui.QGraphicsLineItem(0,miny, 0, 0))
+ self.scene.addItem(QtGui.QGraphicsLineItem(0,0, min(maxx, maxy), min(maxx, maxy)))
+ self.scene.addItem(QtGui.QGraphicsLineItem(max(minx,miny), max(minx,miny), 0,0))
+
+ # Dashed, gray integer lattice
+ pen = QtGui.QPen(QtCore.Qt.DashLine)
+ pen.setColor(QtCore.Qt.gray)
+ for i in xrange(min(0, int(minx)) + 1, max(0,int(maxx)) + 1):
+ line = QtGui.QGraphicsLineItem(i,0, i, maxy)
+ line.setPen(pen)
+ self.scene.addItem(line)
+ for i in xrange(min(0, int(miny)) + 1, max(0, int(maxy)) + 1):
+ line = QtGui.QGraphicsLineItem(0,i, maxx, i)
+ line.setPen(pen)
+ self.scene.addItem(line)
+
+
+def show_diagram(dgm, noise, app):
+ #app = QtGui.QApplication([])
+ view = DiagramViewer(dgm, noise)
+ view.show()
+ view.raise_()
+ app.exec_()
+ return view.selection
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/zigzag.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,20 @@
+def add_simplices(zz, simplices, complex, birth, report_local = False):
+ deaths = []
+ for s in simplices:
+ i,d = zz.add([complex[sb] for sb in s.boundary], (s.dimension(), birth))
+ complex[s] = i
+ if d is not None:
+ if report_local or not d[1] == birth:
+ deaths.append(d)
+ return deaths
+
+
+def remove_simplices(zz, simplices, complex, birth, report_local = False):
+ deaths = []
+ for s in simplices:
+ d = zz.remove(complex[s], (s.dimension() - 1, birth))
+ complex[s] = None
+ if d is not None:
+ if report_local or not d[1] == birth:
+ deaths.append(d)
+ return deaths
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/distances.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,21 @@
+#include <boost/python.hpp>
+namespace bp = boost::python;
+
+#include "distances.h"
+namespace dp = dionysus::python;
+
+boost::shared_ptr<dp::ListPointPairwiseDistances> init_from_list(bp::list lst)
+{
+ boost::shared_ptr<dp::ListPointPairwiseDistances> p(new dp::ListPointPairwiseDistances(lst));
+ return p;
+}
+
+void export_pairwise_distances()
+{
+ bp::class_<dp::ListPointPairwiseDistances>("PairwiseDistances", bp::no_init)
+ .def("__init__", bp::make_constructor(&init_from_list))
+ .def("__len__", &dp::ListPointPairwiseDistances::size)
+ .def("__call__", &dp::ListPointPairwiseDistances::operator())
+ ;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/distances.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,54 @@
+#include <utilities/log.h>
+
+#include <boost/python.hpp>
+namespace bp = boost::python;
+
+namespace dionysus {
+namespace python {
+
+typedef bp::list ListPoint;
+
+struct ListPointL2Distance:
+ public std::binary_function<bp::object, bp::object, double>
+{
+ result_type operator()(bp::object p1, bp::object p2) const
+ {
+ ListPoint lp1 = bp::extract<ListPoint>(p1), lp2 = bp::extract<ListPoint>(p2);
+
+ AssertMsg(bp::len(lp1) == bp::len(lp2), "Points must be in the same dimension (in L2Distance): dim1=%d, dim2=%d", bp::len(lp1), bp::len(lp2));
+ result_type sum = 0;
+ for (size_t i = 0; i < bp::len(lp1); ++i)
+ {
+ double diff = bp::extract<double>(lp1[i]) - bp::extract<double>(lp2[i]);
+ sum += diff*diff;
+ }
+
+ return sqrt(sum);
+ }
+};
+
+class ListPointPairwiseDistances
+{
+ public:
+ typedef bp::list Container;
+ typedef ListPointL2Distance Distance;
+ typedef unsigned IndexType;
+ typedef Distance::result_type DistanceType;
+
+
+ ListPointPairwiseDistances(Container container):
+ container_(container) {}
+
+ DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); }
+
+ size_t size() const { return bp::len(container_); }
+ IndexType begin() const { return 0; }
+ IndexType end() const { return size(); }
+
+ private:
+ Container container_;
+ Distance distance_;
+};
+
+} } // namespace dionysus::python
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dynamic-persistence.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,41 @@
+#include <topology/dynamic-persistence.h>
+
+#include <boost/python.hpp>
+#include <boost/python/iterator.hpp>
+#include <boost/python/return_internal_reference.hpp>
+namespace bp = boost::python;
+
+#include "filtration.h"
+#include "dynamic-persistence.h"
+#include "chain.h"
+namespace dp = dionysus::python;
+
+
+dp::DPersistenceChains::iterator dpc_begin(dp::DPersistenceChains& dpc) { return dpc.begin(); }
+dp::DPersistenceChains::iterator dpc_end(dp::DPersistenceChains& dpc) { return dpc.end(); }
+
+void export_dynamic_persistence_chains()
+{
+ bp::class_<dp::DPersistenceChainsNode>("DPCNode", bp::no_init)
+ .def("pair", &dp::pair<dp::DPersistenceChainsNode>, bp::return_internal_reference<1>())
+ .add_property("cycle", &dp::DPersistenceChainsNode::cycle)
+ .add_property("chain", &dp::DPersistenceChainsNode::chain)
+ .def("sign", &dp::DPersistenceChainsNode::sign)
+ .def("unpaired", &dp::DPersistenceChainsNode::unpaired)
+ ;
+
+ bp::class_<dp::DPersistenceChains>("DynamicPersistenceChains", bp::no_init)
+ .def("__init__", bp::make_constructor(&dp::init_from_filtration<dp::DPersistenceChains>))
+
+ .def("pair_simplices", &dp::DPersistenceChains::pair_simplices)
+ .def("__call__", &dp::distance<dp::DPersistenceChains, dp::DPersistenceChainsIndex>)
+ .def("make_simplex_map",&dp::DPersistenceChains::make_simplex_map<dp::PythonFiltration>)
+
+ .def("__iter__", bp::range<bp::return_internal_reference<1> >(dpc_begin, dpc_end))
+ .def("__len__", &dp::DPersistenceChains::size)
+ ;
+
+ bp::class_<dp::DPersistenceChainsSimplexMap>("DPersistenceChainsSimplexMap", bp::no_init)
+ .def("__getitem__", &dp::psmap_getitem<dp::DPersistenceChainsSimplexMap, dp::DPersistenceChainsIndex>, bp::return_internal_reference<1>())
+ ;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dynamic-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,17 @@
+#ifndef __PYTHON_DYNAMIC_PERSISTENCE_CHAINS_H__
+#define __PYTHON_DYNAMIC_PERSISTENCE_CHAINS_H__
+
+#include <topology/dynamic-persistence.h>
+#include "static-persistence.h"
+
+namespace dionysus {
+namespace python {
+
+typedef DynamicPersistenceChains<> DPersistenceChains;
+typedef DPersistenceChains::OrderElement DPersistenceChainsNode;
+typedef DPersistenceChains::OrderIndex DPersistenceChainsIndex;
+typedef DPersistenceChains::SimplexMap<PythonFiltration>
+ DPersistenceChainsSimplexMap;
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/filtration.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,60 @@
+#include <topology/filtration.h>
+
+#include <boost/python.hpp>
+#include <boost/iterator.hpp>
+#include <boost/python/return_internal_reference.hpp>
+namespace bp = boost::python;
+
+
+#include "simplex.h"
+#include "filtration.h" // defines PythonFiltration
+#include "utils.h" // defines PythonCmp
+namespace dp = dionysus::python;
+
+boost::shared_ptr<dp::PythonFiltration> init_from_iterator(bp::object iter)
+{
+ typedef dp::PythonFiltration::Simplex Smplx;
+ boost::shared_ptr<dp::PythonFiltration> p(new dp::PythonFiltration(bp::stl_input_iterator<Smplx>(iter),
+ bp::stl_input_iterator<Smplx>()));
+ return p;
+}
+
+boost::shared_ptr<dp::PythonFiltration> init_from_iterator_cmp(bp::object iter, bp::object cmp)
+{
+ typedef dp::PythonFiltration::Simplex Smplx;
+ boost::shared_ptr<dp::PythonFiltration> p(new dp::PythonFiltration(bp::stl_input_iterator<Smplx>(iter),
+ bp::stl_input_iterator<Smplx>(),
+ dp::PythonCmp(cmp)));
+ return p;
+}
+
+void filtration_sort(dp::PythonFiltration& f, bp::object cmp)
+{ f.sort(dp::PythonCmp(cmp)); }
+
+const dp::PythonFiltration::Simplex& f_getitem(const dp::PythonFiltration& f, int i)
+{
+ if (i >= 0)
+ return f.simplex(f.begin() + i);
+ else
+ return f.simplex(f.end() + i);
+}
+
+unsigned f_call(const dp::PythonFiltration& f, const dp::PythonFiltration::Simplex& s)
+{ return f.find(s) - f.begin(); }
+
+
+void export_filtration()
+{
+ bp::class_<dp::PythonFiltration>("Filtration")
+ .def("__init__", bp::make_constructor(&init_from_iterator))
+ .def("__init__", bp::make_constructor(&init_from_iterator_cmp))
+
+ .def("append", &dp::PythonFiltration::push_back)
+ .def("sort", &filtration_sort)
+
+ .def("__getitem__", &f_getitem, bp::return_internal_reference<1>())
+ .def("__call__", &f_call)
+ .def("__iter__", bp::range<bp::return_internal_reference<1> >(&dp::PythonFiltration::begin, &dp::PythonFiltration::end))
+ .def("__len__", &dp::PythonFiltration::size)
+ ;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/filtration.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,18 @@
+#ifndef __PYTHON_FILTRATION_H__
+#define __PYTHON_FILTRATION_H__
+
+#include <topology/filtration.h>
+#include <boost/python.hpp>
+#include "simplex.h"
+#include "utils.h" // for ListRandomAccessIterator
+
+namespace bp = boost::python;
+
+namespace dionysus {
+namespace python {
+
+typedef Filtration<SimplexVD> PythonFiltration;
+
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/optional.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,85 @@
+#ifndef __PYTHON_OPTIONAL_H__
+#define __PYTHON_OPTIONAL_H__
+
+#include <boost/python.hpp>
+#include <boost/optional.hpp>
+#include <boost/utility.hpp>
+
+// Taken from an email by John Wiegley;
+// http://mail.python.org/pipermail/cplusplus-sig/2007-May/012003.html
+
+template <typename T, typename TfromPy>
+struct object_from_python
+{
+ object_from_python()
+ {
+ boost::python::converter::registry::push_back(&TfromPy::convertible,
+ &TfromPy::construct,
+ boost::python::type_id<T>());
+ }
+};
+
+template <typename T, typename TtoPy, typename TfromPy>
+struct register_python_conversion
+{
+ register_python_conversion()
+ {
+ boost::python::to_python_converter<T, TtoPy>();
+ object_from_python<T, TfromPy>();
+ }
+};
+
+template <typename T>
+struct python_optional : public boost::noncopyable
+{
+ struct optional_to_python
+ {
+ static PyObject * convert(const boost::optional<T>& value)
+ {
+ return (value ? boost::python::to_python_value<T>()(*value) :
+ boost::python::detail::none());
+ }
+ };
+
+ struct optional_from_python
+ {
+ static void * convertible(PyObject * source)
+ {
+ using namespace boost::python::converter;
+
+ if (source == Py_None)
+ return source;
+
+ const registration& converters(registered<T>::converters);
+
+ if (implicit_rvalue_convertible_from_python(source, converters))
+ {
+ rvalue_from_python_stage1_data data = rvalue_from_python_stage1(source, converters);
+ return rvalue_from_python_stage2(source, data, converters);
+ }
+ return NULL;
+ }
+
+ static void construct(PyObject * source,
+ boost::python::converter::rvalue_from_python_stage1_data * data)
+ {
+ using namespace boost::python::converter;
+
+ void * const storage = ((rvalue_from_python_storage<T> *) data)->storage.bytes;
+
+ if (data->convertible == source) // == None
+ new (storage) boost::optional<T>(); // A Boost uninitialized value
+ else
+ new (storage) boost::optional<T>(*static_cast<T *>(data->convertible));
+
+ data->convertible = storage;
+ }
+ };
+
+ explicit python_optional()
+ {
+ register_python_conversion<boost::optional<T>, optional_to_python, optional_from_python>();
+ }
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/persistence-diagram.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,229 @@
+#include<topology/persistence-diagram.h>
+#include<utilities/types.h>
+
+#include "filtration.h"
+#include "simplex.h"
+#include "static-persistence.h"
+#include "dynamic-persistence.h"
+
+#include <boost/foreach.hpp>
+
+#include<boost/python.hpp>
+#include<boost/python/init.hpp>
+#include<boost/shared_ptr.hpp>
+#include<boost/python/stl_iterator.hpp>
+#include<boost/python/def.hpp>
+#include<boost/python/register_ptr_to_python.hpp>
+namespace bp = boost::python;
+
+
+namespace dionysus{
+namespace python{
+
+typedef bp::object Data;
+typedef PersistenceDiagram<Data> PersistenceDiagramD;
+typedef PersistenceDiagramD::Point PointD;
+typedef boost::shared_ptr<PersistenceDiagramD> PDgmPtr;
+
+} } //namespace dionysus::python
+
+namespace dp = dionysus::python;
+
+struct PointFromTupleConverter
+{
+ PointFromTupleConverter()
+ {
+ boost::python::converter::registry::push_back(&convertible,
+ &construct,
+ boost::python::type_id<dp::PointD>());
+ }
+
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyTuple_Check(obj_ptr)) return 0;
+ if (PyTuple_Size(obj_ptr) < 2) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr,
+ boost::python::converter::rvalue_from_python_stage1_data* data)
+ {
+ //const char* value = PyString_AsString(obj_ptr);
+ //if (value == 0) boost::python::throw_error_already_set();
+
+ // Grab pointer to memory into which to construct the new T
+ void* storage = ( (boost::python::converter::rvalue_from_python_storage<dp::PointD>*) data)->storage.bytes;
+
+ RealType x = bp::extract<RealType>(PyTuple_GetItem(obj_ptr, 0));
+ RealType y = bp::extract<RealType>(PyTuple_GetItem(obj_ptr, 1));
+
+ // in-place construct the new T using the character data extraced from the python object
+ dp::PointD* p = new (storage) dp::PointD(x,y);
+
+ if (PyTuple_Size(obj_ptr) > 2)
+ p->data() = bp::extract<bp::object>(PyTuple_GetItem(obj_ptr, 2));
+
+ // Stash the memory chunk pointer for later use by boost.python
+ data->convertible = storage;
+ }
+};
+
+struct PointToTupleConverter
+{
+ static PyObject* convert(const dp::PointD& p)
+ {
+ if (p.data().ptr() == bp::object().ptr())
+ return bp::incref(bp::make_tuple(p.x(), p.y()).ptr());
+ else
+ return bp::incref(bp::make_tuple(p.x(), p.y(), p.data()).ptr());
+ }
+};
+
+
+void export_point( )
+{
+ PointFromTupleConverter();
+ bp::to_python_converter<dp::PointD, PointToTupleConverter>();
+}
+
+
+boost::shared_ptr<dp::PersistenceDiagramD> init_from_points_sequence(Dimension dimension, bp::object point_sequence)
+{
+ typedef bp::stl_input_iterator<dp::PointD> PointIterator;
+
+ PointIterator beg = PointIterator(point_sequence), end = PointIterator();
+ boost::shared_ptr<dp::PersistenceDiagramD> p(new dp::PersistenceDiagramD(dimension));
+
+ for(PointIterator cur = beg; cur != end; cur++)
+ (*p).push_back(*cur);
+ return p;
+
+}
+
+RealType bottleneck_distance_adapter(const dp::PersistenceDiagramD& dgm1, const dp::PersistenceDiagramD& dgm2)
+{
+ return bottleneck_distance(dgm1, dgm2);
+}
+
+
+template<class Persistence>
+struct InitDiagrams
+{
+ typedef std::map<int, dp::PDgmPtr> DiagramMap;
+ typedef typename Persistence::template SimplexMap<dp::PythonFiltration> SMap;
+
+ struct DataEvaluator
+ {
+ DataEvaluator(const SMap& smap_):
+ smap(smap_) {}
+
+ template<class Key>
+ RealType operator()(Key k) const { return bp::extract<RealType>(smap[k].data()); }
+
+ const SMap& smap;
+ };
+
+ struct PythonEvaluator
+ {
+ PythonEvaluator(const SMap& smap_, bp::object eval_):
+ smap(smap_), eval(eval_) {}
+
+ template<class Key>
+ RealType operator()(Key k) const { return bp::extract<RealType>(eval(smap[k])); }
+
+ const SMap& smap;
+ bp::object eval;
+ };
+
+ // A hack
+ struct DiagramMapOwner: public DiagramMap
+ {
+ typedef dp::PersistenceDiagramD mapped_type;
+
+ mapped_type& operator[](Dimension d)
+ {
+ if (this->find(d) == this->end())
+ this->insert(std::make_pair(d, dp::PDgmPtr(new dp::PersistenceDiagramD(d))));
+ return *DiagramMap::operator[](d);
+ }
+ };
+
+ static
+ bp::list extract_list(const DiagramMapOwner& dgms)
+ {
+ bp::list result;
+ size_t dim = 0;
+ typedef typename DiagramMapOwner::value_type ValType;
+ BOOST_FOREACH(const ValType& dim_dgm, dgms)
+ {
+ while (dim_dgm.first > dim)
+ {
+ result.append(dp::PDgmPtr(new dp::PersistenceDiagramD));
+ ++dim;
+ }
+
+ // dim_dgm.first == dim
+ result.append(dim_dgm.second);
+ dim++;
+ }
+
+ return result;
+ }
+
+ struct PointDataVisitor
+ {
+ PointDataVisitor(bp::object data_): data(data_) {}
+ void point(const typename Persistence::iterator& i, dp::PointD& p) const { p.data() = data(*i); }
+ bp::object data;
+ };
+
+ static
+ bp::list init(const Persistence& p, const dp::PythonFiltration& f, bp::object eval, bp::object data)
+ {
+
+ DiagramMapOwner dgms;
+ SMap smap = p.make_simplex_map(f);
+ if (eval == bp::object())
+ init_diagrams(dgms, p.begin(), p.end(),
+ DataEvaluator(smap),
+ evaluate_through_map(smap, dp::SimplexVD::DimensionExtractor()));
+ else if (data == bp::object())
+ init_diagrams(dgms, p.begin(), p.end(),
+ PythonEvaluator(smap, eval),
+ evaluate_through_map(smap, dp::SimplexVD::DimensionExtractor()));
+ else
+ init_diagrams(dgms, p.begin(), p.end(),
+ PythonEvaluator(smap, eval),
+ evaluate_through_map(smap, dp::SimplexVD::DimensionExtractor()),
+ PointDataVisitor(data));
+ return extract_list(dgms);
+ }
+};
+
+void export_persistence_diagram()
+{
+ bp::class_<dp::PersistenceDiagramD, dp::PDgmPtr>("PersistenceDiagram")
+ .def("__init__", bp::make_constructor(&init_from_points_sequence))
+ .def( bp::init<Dimension>())
+ .def("append", &dp::PersistenceDiagramD::push_back)
+ .add_property("dimension", &dp::PersistenceDiagramD::dimension)
+ .def( repr(bp::self))
+ .def("__iter__", bp::range(&dp::PersistenceDiagramD::begin, &dp::PersistenceDiagramD::end))
+ .def("__len__", &dp::PersistenceDiagramD::size)
+ ;
+ bp::register_ptr_to_python<dp::PDgmPtr>();
+
+ bp::def("init_diagrams", &InitDiagrams<dp::SPersistence>::init,
+ (bp::arg("persistence"),
+ bp::arg("filtration"),
+ bp::arg("eval")=bp::object(),
+ bp::arg("data")=bp::object()));
+ bp::def("init_diagrams", &InitDiagrams<dp::DPersistenceChains>::init,
+ (bp::arg("persistence"),
+ bp::arg("filtration"),
+ bp::arg("eval")=bp::object(),
+ bp::arg("data")=bp::object()));
+
+ bp::def("bottleneck_distance", &bottleneck_distance_adapter);
+ bp::def("wasserstein_distance", &wasserstein_distance<dp::PersistenceDiagramD>);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/rips.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,35 @@
+#include <topology/rips.h>
+#include <boost/python.hpp>
+namespace bp = boost::python;
+
+#include "rips.h" // defines RipsWithDistances
+namespace dp = dionysus::python;
+
+#include <iostream>
+
+
+/* Various wrappers for exposing Rips to Python */
+// Constructor from distances
+boost::shared_ptr<dp::RipsWithDistances> init_from_distances(bp::object distances)
+{
+ boost::shared_ptr<dp::RipsWithDistances> p(new dp::RipsWithDistances(distances));
+ return p;
+}
+
+void export_rips()
+{
+ bp::class_<dp::RipsWithDistances>("Rips", bp::no_init)
+ .def("__init__", bp::make_constructor(&init_from_distances))
+ .def("generate", &dp::RipsWithDistances::generate)
+ .def("generate", &dp::RipsWithDistances::generate_candidates)
+ .def("vertex_cofaces", &dp::RipsWithDistances::vertex_cofaces)
+ .def("vertex_cofaces", &dp::RipsWithDistances::vertex_cofaces_candidate)
+ .def("edge_cofaces", &dp::RipsWithDistances::edge_cofaces)
+ .def("edge_cofaces", &dp::RipsWithDistances::edge_cofaces_candidates)
+
+ .def("cmp", &dp::RipsWithDistances::cmp)
+ .def("cmp", &dp::RipsWithDistances::cmp_native)
+ .def("eval", &dp::RipsWithDistances::eval)
+ .def("eval", &dp::RipsWithDistances::eval_native)
+ ;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/rips.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,112 @@
+#ifndef __PYTHON_RIPS_H__
+#define __PYTHON_RIPS_H__
+
+#include <topology/rips.h>
+#include <utilities/indirect.h>
+
+#include "simplex.h"
+
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+
+
+namespace bp = boost::python;
+
+
+namespace dionysus {
+namespace python {
+
+// This strange wrapper is necessary because Rips<...> stores only a const reference to the distances.
+// Something on the C++ side of things must store the actual DistancesWrapper object, and that's the
+// purpose of this class.
+class RipsWithDistances
+{
+ public:
+ class DistancesWrapper
+ {
+ public:
+ typedef unsigned IndexType;
+ typedef double DistanceType;
+
+ DistancesWrapper(bp::object distances):
+ distances_(distances) {}
+
+ DistanceType operator()(IndexType a, IndexType b) const { return bp::extract<DistanceType>(distances_(a, b)); }
+
+ IndexType size() const { return bp::len(distances_); }
+ IndexType begin() const { return 0; }
+ IndexType end() const { return size(); }
+
+ private:
+ bp::object distances_;
+ };
+
+ typedef DistancesWrapper::IndexType IndexType;
+ typedef DistancesWrapper::DistanceType DistanceType;
+
+ typedef Rips<DistancesWrapper, SimplexVD> RipsDS;
+ typedef RipsDS::Comparison Comparison;
+ typedef RipsDS::Evaluator Evaluator;
+
+ class FunctorWrapper
+ {
+ public:
+ FunctorWrapper(bp::object functor):
+ functor_(functor) {}
+
+ void operator()(const RipsDS::Simplex& s) const { functor_(s); }
+
+ private:
+ bp::object functor_;
+ };
+
+
+ RipsWithDistances(bp::object distances):
+ distances_(distances), rips_(distances_),
+ cmp_(Comparison(distances_)), eval_(distances_) {}
+
+ void generate(Dimension k, DistanceType max, bp::object functor) const
+ { rips_.generate(k, max, FunctorWrapper(functor)); }
+
+ void vertex_cofaces(IndexType v, Dimension k, DistanceType max, bp::object functor) const
+ { rips_.vertex_cofaces(v, k, max, FunctorWrapper(functor)); }
+
+ void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, bp::object functor) const
+ { rips_.edge_cofaces(u, v, k, max, FunctorWrapper(functor)); }
+
+ void generate_candidates(Dimension k, DistanceType max, bp::object functor, bp::object seq) const
+ {
+ rips_.generate(k, max, FunctorWrapper(functor),
+ bp::stl_input_iterator<IndexType>(seq), bp::stl_input_iterator<IndexType>());
+ }
+
+ void vertex_cofaces_candidate(IndexType v, Dimension k, DistanceType max,
+ bp::object functor, bp::object seq) const
+ {
+ rips_.vertex_cofaces(v, k, max, FunctorWrapper(functor),
+ bp::stl_input_iterator<IndexType>(seq), bp::stl_input_iterator<IndexType>());
+ }
+
+ void edge_cofaces_candidates(IndexType u, IndexType v, Dimension k, DistanceType max,
+ bp::object functor, bp::object seq) const
+ {
+ rips_.edge_cofaces(u, v, k, max, FunctorWrapper(functor),
+ bp::stl_input_iterator<IndexType>(seq), bp::stl_input_iterator<IndexType>());
+ }
+
+ int cmp(const SimplexObject& s1, const SimplexObject& s2) const { return cmp_native(s1, s2); }
+ int cmp_native(const SimplexVD& s1, const SimplexVD& s2) const { return cmp_.compare(s1, s2); }
+
+ DistanceType eval(const SimplexObject& s) const { return eval_native(s); }
+ DistanceType eval_native(const SimplexVD& s) const { return eval_(s); }
+
+ private:
+ DistancesWrapper distances_;
+ RipsDS rips_;
+ ThreeOutcomeCompare<Comparison> cmp_; // in Python, cmp is a three outcome comparison
+ Evaluator eval_;
+};
+
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/simplex.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,98 @@
+#include <topology/simplex.h>
+#include <utilities/indirect.h>
+#include <iostream>
+
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/functional/hash.hpp>
+namespace bp = boost::python;
+
+#include "simplex.h" // defines SimplexVD, Vertex, and Data
+namespace dp = dionysus::python;
+
+
+/* Various wrappers for exposing Simplex to Python */
+// `vertices` property
+template<class V, class T>
+typename Simplex<V,T>::VertexContainer::const_iterator
+ vertices_begin(const Simplex<V,T>& s) { return s.vertices().begin(); }
+template<class V, class T>
+typename Simplex<V,T>::VertexContainer::const_iterator
+ vertices_end(const Simplex<V,T>& s) { return s.vertices().end(); }
+
+// Constructor from iterator TODO: the default argument is not working yet
+template<class V, class T>
+boost::shared_ptr<Simplex<V,T> > init_from_iterator(bp::object iter, bp::object d)
+{
+ boost::shared_ptr<Simplex<V,T> > p(new Simplex<V,T>(bp::stl_input_iterator<V>(iter), bp::stl_input_iterator<V>(), d));
+ return p;
+}
+
+
+// Simplex hash
+template<class V, class T>
+size_t hash_simplex(const Simplex<V,T>& s)
+{
+ return boost::hash_range(s.vertices().begin(), s.vertices().end());
+}
+
+template<class V, class T>
+size_t eq_simplex(const Simplex<V,T>& a, const Simplex<V,T>& b)
+{
+ return vertex_comparison(a,b) == 0;
+}
+
+template<class S>
+bool contains(const S& s, const S& other)
+{
+ return s.contains(other);
+}
+
+template<class S>
+dp::Data get_data(const S& s)
+{
+ return s.data();
+}
+
+template<class S>
+void set_data(S& s, dp::Data d)
+{
+ s.data() = d;
+}
+
+/* Comparisons */
+// VertexComparison
+template<class V, class T>
+int vertex_comparison(const Simplex<V,T>& a, const Simplex<V,T>& b)
+{
+ return ThreeOutcomeCompare<typename Simplex<V,T>::VertexComparison>().compare(a,b);
+}
+
+
+void export_simplex()
+{
+ bp::class_<dp::SimplexVD>("Simplex")
+ .def("__init__", bp::make_constructor(&init_from_iterator<dp::Vertex, dp::Data>))
+
+ .def("add", &dp::SimplexVD::add)
+ .add_property("boundary", bp::range(&dp::SimplexVD::boundary_begin, &dp::SimplexVD::boundary_end))
+ .def("contains", &contains<dp::SimplexVD>)
+ .def("join", (void (dp::SimplexVD::*)(const dp::SimplexVD&)) &dp::SimplexVD::join)
+ .def("dimension", &dp::SimplexVD::dimension)
+ .add_property("data", &get_data<dp::SimplexVD>, &set_data<dp::SimplexVD>)
+
+ .add_property("vertices", bp::range(&vertices_begin<dp::Vertex, dp::Data>, &vertices_end<dp::Vertex, dp::Data>))
+ .def(repr(bp::self))
+
+ .def("__hash__", &hash_simplex<dp::Vertex, dp::Data>)
+ .def("__eq__", &eq_simplex<dp::Vertex, dp::Data>)
+ .enable_pickling()
+ ;
+
+ bp::class_<dp::SimplexObject>("SimplexObject")
+ .def("__getattribute__", &dp::SimplexObject::getattribute)
+ ;
+
+ bp::def("vertex_cmp", &vertex_comparison<dp::Vertex, dp::Data>);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/simplex.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,65 @@
+#ifndef __PYTHON_SIMPLEX_H__
+#define __PYTHON_SIMPLEX_H__
+
+#include <topology/simplex.h>
+
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+namespace bp = boost::python;
+
+
+namespace dionysus {
+namespace python {
+
+/**
+ * SimplexVD is a base class for Python simplices (it's exposed to python as Simplex)
+ *
+ * SimplexObject is the representation of Python simplices in C++; i.e. it wraps bp::object and exposes a simplex-like interface.
+ */
+typedef int Vertex;
+typedef bp::object Data;
+typedef Simplex<Vertex, Data> SimplexVD;
+
+
+// Wrapper around bp::object that acts like a simplex
+class SimplexObject: public bp::object
+{
+ public:
+ typedef SimplexObject Self;
+ typedef bp::object Parent;
+ typedef bp::stl_input_iterator<Self> BoundaryIterator;
+
+
+ SimplexObject(Parent o = Parent()): Parent(o) {}
+
+ BoundaryIterator boundary_begin() const { return bp::stl_input_iterator<Self>(this->attr("boundary")); }
+ BoundaryIterator boundary_end() const { return bp::stl_input_iterator<Self>(); }
+
+ operator SimplexVD() const { return bp::extract<const SimplexVD&>(*this); }
+ operator bp::object() const { return *this; }
+
+ bp::object getattribute(const char* name) const { return this->attr(name); }
+
+ class VertexComparison: public SimplexVD::VertexComparison
+ {
+ public:
+ typedef Self first_argument_type;
+ typedef Self second_argument_type;
+ typedef bool result_type;
+
+ bool operator()(const SimplexObject& s1, const SimplexObject& s2) const
+ { return SimplexVD::VertexComparison::operator()(bp::extract<const SimplexVD&>(s1), bp::extract<const SimplexVD&>(s2)); }
+ };
+};
+
+struct SimplexObjectToSimplexVD
+{
+ static PyObject* convert (const SimplexObject& so)
+ {
+ return (PyObject*) &so;
+ }
+};
+
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/static-persistence.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,44 @@
+#include <topology/static-persistence.h>
+
+#include <boost/python.hpp>
+#include <boost/python/iterator.hpp>
+#include <boost/python/return_internal_reference.hpp>
+namespace bp = boost::python;
+
+#include "filtration.h"
+#include "static-persistence.h"
+#include "chain.h"
+namespace dp = dionysus::python;
+
+
+void pair_simplices(dp::SPersistence& sp, bool store_negative)
+{
+ dp::SPersistence::PairVisitorNoProgress visitor;
+ sp.pair_simplices(sp.begin(), sp.end(), store_negative, visitor);
+}
+
+
+void export_static_persistence()
+{
+ bp::class_<dp::SPersistenceNode>("SPNode", bp::no_init)
+ .def("pair", &dp::pair<dp::SPersistenceNode>, bp::return_internal_reference<1>())
+ .add_property("cycle", &dp::SPersistenceNode::cycle)
+ .def("sign", &dp::SPersistenceNode::sign)
+ .def("unpaired", &dp::SPersistenceNode::unpaired)
+ ;
+
+ bp::class_<dp::SPersistence>("StaticPersistence", bp::no_init)
+ .def("__init__", bp::make_constructor(&dp::init_from_filtration<dp::SPersistence>))
+
+ .def("pair_simplices", &pair_simplices, (bp::args("store_negative")=false))
+ .def("__call__", &dp::distance<dp::SPersistence, dp::SPersistenceIndex>)
+ .def("make_simplex_map",&dp::SPersistence::make_simplex_map<dp::PythonFiltration>)
+
+ .def("__iter__", bp::range<bp::return_internal_reference<1> >(&dp::SPersistence::begin, &dp::SPersistence::end))
+ .def("__len__", &dp::SPersistence::size)
+ ;
+
+ bp::class_<dp::SPersistenceSimplexMap>("SPersistenceSimplexMap", bp::no_init)
+ .def("__getitem__", &dp::psmap_getitem<dp::SPersistenceSimplexMap, dp::SPersistenceIndex>, bp::return_internal_reference<1>())
+ ;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/static-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,44 @@
+#ifndef __PYTHON_STATIC_PERSISTENCE_H__
+#define __PYTHON_STATIC_PERSISTENCE_H__
+
+#include <topology/static-persistence.h>
+
+#include "filtration.h"
+
+namespace dionysus {
+namespace python {
+
+typedef StaticPersistence<> SPersistence;
+typedef SPersistence::OrderElement SPersistenceNode;
+typedef SPersistence::OrderIndex SPersistenceIndex;
+typedef SPersistence::SimplexMap<PythonFiltration>
+ SPersistenceSimplexMap;
+
+
+/* Persistence */
+template<class Persistence>
+boost::shared_ptr<Persistence> init_from_filtration(bp::object f)
+{
+ PythonFiltration& sf = bp::extract<PythonFiltration&>(f);
+ boost::shared_ptr<Persistence> p(new Persistence(sf));
+ return p;
+}
+
+template<class Persistence, class PersistenceIndex>
+unsigned distance(Persistence& p,
+ const PersistenceIndex& i) { return p.iterator_to(i) - p.begin(); }
+
+/* SPNode */
+template<class PNode>
+const PNode& pair(const PNode& n) { return *n.pair; }
+
+
+/* PersistenceSimplexMap */
+template<class PersistenceSimplexMap, class PersistenceIndex>
+const SimplexVD& psmap_getitem(const PersistenceSimplexMap& psmap,
+ const PersistenceIndex& i) { return psmap[i]; }
+
+
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/utils.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,62 @@
+#ifndef __PYTHON_UTILS_H__
+#define __PYTHON_UTILS_H__
+
+#include <boost/python.hpp>
+#include <boost/iterator/counting_iterator.hpp>
+namespace bp = boost::python;
+
+namespace dionysus {
+namespace python {
+
+// Random access iterator into python's list (using integer indices)
+template<class Value>
+class ListRandomAccessIterator:
+ public boost::iterator_adaptor<ListRandomAccessIterator<Value>, // Derived
+ boost::counting_iterator<unsigned>, // Base
+ Value, // Value
+ boost::use_default,
+ Value>
+{
+ public:
+ typedef ListRandomAccessIterator Self;
+ typedef boost::iterator_adaptor<ListRandomAccessIterator,
+ boost::counting_iterator<unsigned>,
+ Value,
+ boost::use_default,
+ Value> Parent;
+
+ ListRandomAccessIterator() {}
+
+ ListRandomAccessIterator(bp::list l, unsigned i):
+ Parent(i), l_(l) {}
+
+ private:
+ friend class boost::iterator_core_access;
+ friend class FiltrationPythonIterator;
+
+ typename Parent::reference
+ dereference() const { return bp::object(l_[*(this->base())]); }
+
+ bp::list l_;
+};
+
+// Adaptor of a Pyhon object to act as a C++-style comparison functor
+struct PythonCmp
+{
+ template<class T>
+ bool operator()(T x1, T x2) const { return cmp_(x1, x2) < 0; }
+
+ PythonCmp(bp::object cmp): cmp_(cmp) {}
+
+ bp::object cmp_;
+};
+
+template<class T1, class T2>
+struct PairToTupleConverter
+{
+ static PyObject* convert(const std::pair<T1, T2>& pair) { return bp::incref(bp::make_tuple(pair.first, pair.second).ptr()); }
+};
+
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/zigzag-persistence.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,128 @@
+#include <topology/zigzag-persistence.h>
+#include <topology/image-zigzag-persistence.h>
+
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+#include <boost/shared_ptr.hpp>
+namespace bp = boost::python;
+
+#include "zigzag-persistence.h" // defines ZZPersistence, IZZPersistence
+#include "optional.h"
+namespace dp = dionysus::python;
+
+#include <sstream>
+#include <string>
+
+
+// ZigzagPersistence
+bp::tuple zzp_add(dp::ZZPersistence& zzp, bp::object bdry, dp::BirthID birth)
+{
+ // Make ZColumn
+ // NB: it's extremely weird that I have to do it this way,
+ // but for some reason I cannot just create boundary on the stack
+ boost::shared_ptr<dp::ZZPersistence::ZColumn>
+ boundary(new dp::ZZPersistence::ZColumn(bp::stl_input_iterator<dp::ZZPersistence::SimplexIndex>(bdry),
+ bp::stl_input_iterator<dp::ZZPersistence::SimplexIndex>()));
+ boundary->sort(zzp.cmp);
+
+ dp::ZZPersistence::SimplexIndex i;
+ dp::ZZPersistence::Death d;
+ boost::tie(i,d) = zzp.add(*boundary, birth);
+ return bp::make_tuple(i,d);
+}
+
+dp::ZZPersistence::Death zzp_remove(dp::ZZPersistence& zzp, dp::ZZPersistence::SimplexIndex s, dp::ZZPersistence::BirthID birth)
+{
+ return zzp.remove(s, birth);
+}
+
+bool zzp_is_alive(dp::ZZPersistence& zzp, const dp::ZZPersistence::ZNode& zn)
+{
+ return zzp.is_alive(zn);
+}
+
+// ImageZigzagPersistence
+bp::tuple izzp_add(dp::IZZPersistence& izzp, bp::object bdry, bool subcomplex, dp::BirthID birth)
+{
+ // Make ZColumn
+ // NB: it's extremely weird that I have to do it this way,
+ // but for some reason I cannot just create boundary on the stack
+ boost::shared_ptr<dp::IZZPersistence::ZColumn>
+ boundary(new dp::IZZPersistence::ZColumn(bp::stl_input_iterator<dp::IZZPersistence::SimplexIndex>(bdry),
+ bp::stl_input_iterator<dp::IZZPersistence::SimplexIndex>()));
+ boundary->sort(izzp.cmp);
+
+ dp::IZZPersistence::SimplexIndex i;
+ dp::IZZPersistence::Death d;
+ boost::tie(i,d) = izzp.add(*boundary, subcomplex, birth);
+ return bp::make_tuple(i,d);
+}
+
+dp::IZZPersistence::Death izzp_remove(dp::IZZPersistence& izzp, dp::IZZPersistence::SimplexIndex s, dp::IZZPersistence::BirthID birth)
+{
+ return izzp.remove(s, birth);
+}
+
+
+// SimplexIndex
+template<class T>
+unsigned si_order(T& si)
+{
+ return si->order;
+}
+
+template<class T>
+std::string si_repr(T& si)
+{
+ std::ostringstream out; out << "SimplexIndex <" << si->order << ">";
+ return out.str();
+}
+
+// ZNode
+template<class Persistence>
+typename Persistence::ZColumn::const_iterator
+znode_zcolumn_begin(typename Persistence::ZNode& zn)
+{ return zn.z_column.begin(); }
+
+template<class Persistence>
+typename Persistence::ZColumn::const_iterator
+znode_zcolumn_end(typename Persistence::ZNode& zn)
+{ return zn.z_column.end(); }
+
+
+
+void export_zigzag_persistence()
+{
+ bp::class_<dp::ZZPersistence::SimplexIndex>("ZZSimplexIndex")
+ .def("order", &si_order<dp::ZZPersistence::SimplexIndex>)
+ .def("__repr__", &si_repr<dp::ZZPersistence::SimplexIndex>)
+ ;
+
+ bp::class_<dp::IZZPersistence::SimplexIndex>("IZZSimplexIndex")
+ .def("order", &si_order<dp::IZZPersistence::SimplexIndex>)
+ .def("__repr__", &si_repr<dp::IZZPersistence::SimplexIndex>)
+ ;
+
+ bp::class_<dp::ZZPersistence>("ZigzagPersistence")
+ .def("add", &zzp_add)
+ .def("remove", &zzp_remove)
+ .def("is_alive", &zzp_is_alive)
+ .def("__iter__", bp::range(&dp::ZZPersistence::begin, &dp::ZZPersistence::end))
+ ;
+
+ bp::class_<dp::IZZPersistence>("ImageZigzagPersistence")
+ .def("add", &izzp_add)
+ .def("remove", &izzp_remove)
+ .def("__iter__", bp::range(&dp::IZZPersistence::image_begin, &dp::IZZPersistence::image_end))
+ ;
+
+ bp::class_<dp::ZZPersistence::ZNode>("ZNode", bp::no_init)
+ .add_property("birth", &dp::ZZPersistence::ZNode::birth)
+ .def("__iter__", bp::range(&znode_zcolumn_begin<dp::ZZPersistence>, &znode_zcolumn_end<dp::ZZPersistence>))
+ ;
+
+ bp::class_<dp::IZZPersistence::ZNode>("IZNode", bp::no_init)
+ .add_property("birth", &dp::IZZPersistence::ZNode::birth)
+ .def("__iter__", bp::range(&znode_zcolumn_begin<dp::IZZPersistence>, &znode_zcolumn_end<dp::IZZPersistence>))
+ ;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/zigzag-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,18 @@
+#ifndef __PYTHON_ZIGZAG_PERSISTENCE_H__
+#define __PYTHON_ZIGZAG_PERSISTENCE_H__
+
+#include <topology/zigzag-persistence.h>
+#include <topology/image-zigzag-persistence.h>
+#include <boost/python.hpp>
+
+#include "birthid.h"
+
+namespace dionysus {
+namespace python {
+
+typedef ZigzagPersistence<BirthID> ZZPersistence;
+typedef ImageZigzagPersistence<BirthID> IZZPersistence;
+
+} } // namespace dionysus::python
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/Makefile Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,75 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " changes to make an overview over all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+
+clean:
+ -rm -rf .build/*
+
+html:
+ mkdir -p .build/html .build/doctrees
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
+ @echo
+ @echo "Build finished. The HTML pages are in .build/html."
+
+pickle:
+ mkdir -p .build/pickle .build/doctrees
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+web: pickle
+
+json:
+ mkdir -p .build/json .build/doctrees
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) .build/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ mkdir -p .build/htmlhelp .build/doctrees
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in .build/htmlhelp."
+
+latex:
+ mkdir -p .build/latex .build/doctrees
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in .build/latex."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+
+changes:
+ mkdir -p .build/changes .build/doctrees
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes
+ @echo
+ @echo "The overview file is in .build/changes."
+
+linkcheck:
+ mkdir -p .build/linkcheck .build/doctrees
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in .build/linkcheck/output.txt."
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/bibliography.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,34 @@
+Bibliography
+============
+
+.. |edels| replace:: Herbert Edelsbrunner
+.. .. _edels: http://www.cs.duke.edu/~edels/
+.. |david| replace:: David Cohen-Steiner
+.. .. _david: http://www-sop.inria.fr/geometrica/team/David.Cohen-Steiner/
+.. |morozov| replace:: Dmitriy Morozov
+.. .. _morozov: http://www.mrzv.org/
+.. |afra| replace:: Afra Zomorodian
+.. .. _afra: http://www.cs.dartmouth.edu/~afra/
+.. |letscher| replace:: David Letscher
+.. |gunnar| replace:: Gunnar Carlsson
+.. .. _gunnar: http://math.stanford.edu/~gunnar/
+.. |vin| replace:: Vin de Silva
+.. .. _vin: http://pages.pomona.edu/~vds04747/
+.. |mikael| replace:: Mikael Vejdemo Johansson
+
+.. [CdSM09] |gunnar|, |vin|, and |morozov|.
+ `Zigzag Persistent Homology and Real-valued Functions
+ <http://www.mrzv.org/publications/zigzags/>`__.
+
+.. [CEM06] |david|, |edels|, and |morozov|.
+ `Vines and Vineyards by Updating Persistence in Linear Time
+ <http://www.mrzv.org/publications/vineyards/>`__.
+
+.. [dSVJ09] |vin|, |mikael|.
+ Persistent Cohomology and Circular Coordinates.
+
+.. [ELZ02] |edels|, |letscher|, and |afra|.
+ Topological Persistence and Simplification.
+
+.. [ZC05] |afra| and |gunnar|.
+ Computing Persistent Homology.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/conf.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+#
+# Dionysus documentation build configuration file, created by
+# sphinx-quickstart on Tue Dec 9 10:21:40 2008.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+sys.path.append('.')
+
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo',
+ #'sphinx.ext.jsmath', 'ext.sprite_jsmath',
+ 'sphinx.ext.pngmath',
+ 'ext.sfile']
+todo_include_todos = True
+
+jsmath_path = 'jsMath/easy/load.js'
+
+# Base URI for sfile extension
+sfile_base_uri = 'http://hg.mrzv.org/Dionysus/file/tip/'
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Dionysus'
+copyright = u'2005--2009, Dmitriy Morozov'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '3'
+# The full version, including alpha/beta/rc tags.
+release = '3'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['.build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The theme to use for HTML and HTML Help pages. Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+html_title = 'Dionysus'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['.static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Dionysusdoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+ ('index', 'Dionysus.tex', ur'Dionysus Documentation',
+ ur'Dmitriy Morozov', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/examples/alphashape.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,36 @@
+.. _alpha-shape-example:
+
+Alpha shape example
+===================
+
+The example given in :sfile:`examples/alphashapes/alphashapes.py` takes a
+filename containing points in 2D or 3D on the command line. It generates the
+alpha shape filtration of those points, and computes its persistence. It then
+outputs the persistence diagram in the format of a point (dimension, birth,
+death) per line.
+
+.. literalinclude:: ../../examples/alphashapes/alphashapes.py
+ :language: python
+
+After the points are read into the list ``points``, the functions
+:ref:`fill_alpha*_complex <alphashapes>` fill the :class:`Filtration` with the
+simplices of the Delaunay triangulation. Each one has its :attr:`~Simplex.data`
+attribute set to the tuple consisting of its alpha shape value (the minimum value of the squared
+distance function on its dual Voronoi cell) and whether the simplex is regular
+or critical.
+
+The filtration then sorts the simplices with
+respect to their data and dimension (via :func:`data_dim_cmp`)::
+
+ f = Filtration()
+ fill_alpha*_complex(points, f)
+ f.sort(data_dim_cmp)
+
+We initialize :class:`StaticPersistence`, and pair the simplices::
+
+ p = StaticPersistence(f)
+ p.pair_simplices()
+
+Iterating over the :class:`StaticPersistence`, we output the points of the
+persistence diagram (dimension, birth, death) in the last for loop. If the
+simplex is unpaired (``i.unpaired()``), the class it creates survives till infinity.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/examples/cohomology.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,129 @@
+.. _cohomology-parametrization:
+
+Parametrizing a point set using circle valued functions
+=======================================================
+
+The procedure described below is explained in detail in [dSVJ09]_.
+
+.. program:: rips-pairwise-cohomology
+
+One can use :sfile:`examples/cohomology/rips-pairwise-cohomology.cpp` to compute
+persistent pairing of the Rips filtration using the persistent cohomology
+algorithm. It takes as input a file containing a point set in Euclidean space
+(one per line) as well as the following command-line flags:
+
+.. cmdoption:: -p, --prime
+
+ The prime to use in the computation (defaults to 11).
+
+.. cmdoption:: -m, --max-distance
+
+ Maximum cutoff parameter up to which to compute the complex.
+
+.. cmdoption:: -s, --skeleton-dimension
+
+ Skeleton to compute; persistent pairs output will be this number minus 1
+ (defaults to 2).
+
+.. cmdoption:: -b, --boundary
+
+ Filename where to output the boundary matrix.
+
+.. cmdoption:: -c, --cocycle
+
+ Prefix of the filenames where to output the 1-dimensional cocycles.
+
+.. cmdoption:: -v, --vertices
+
+ Filename where to output the simplex vertex mapping.
+
+.. cmdoption:: -d, --diagram
+
+ Filename where to output the persistence diagram.
+
+
+For example::
+
+ rips-pairwise-cohomology points.txt -m 1 -b points.bdry -c points -v points.vrt -d points.dgm
+
+Assuming that at the threshold value of 1 (``-m 1`` above) Rips complex contains
+1-dimensional cocycles, they will be output into filenames of the form
+``points-0.ccl``, ``points-1.ccl``, etc.
+
+Subsequently one can use :sfile:`examples/cohomology/cocycle.py` to assign to
+each vertex of the input point set a circle-valued function. It takes the
+boundary matrix, cocycle, and simplex-vertex map as an input (all produced at
+the previous step)::
+
+ cocycle.py points.bdry points-0.ccl points.vrt
+
+The above command outputs a file ``points-0.val`` which contains values assigned
+to the input points (the lines match the lines of the input file
+``points.txt``, but also contains the indices).
+
+
+Plotting
+--------
+
+Two auxilliary tools allow one to visualize the values assigned to the points
+(using Matplotlib_): :sfile:`tools/plot-values/plot.py` and
+:sfile:`tools/plot-values/scatter.py`::
+
+ plot.py points-0.val points.txt scatter.py points-0.val points-1.val
+
+.. _Matplotlib: http://matplotlib.sourceforge.net/
+
+
+Dependency
+----------
+
+The Python `LSQR code`_ (ported from the `Stanford MATLAB implementation`_ to
+Python by `Jeffery Kline`_) included with Dionysus, and used in
+:sfile:`examples/cohomology/cocycle.py`, requires CVXOPT_.
+
+.. _`LSQR code`: http://pages.cs.wisc.edu/~kline/cvxopt/
+.. _CVXOPT: http://abel.ee.ucla.edu/cvxopt/
+.. _`Stanford MATLAB implementation`: http://www.stanford.edu/group/SOL/software/lsqr.html
+.. _`Jeffery Kline`: http://pages.cs.wisc.edu/~kline/
+
+
+.. _rips-pairwise-cohomology:
+
+Python cohomology computation
+-----------------------------
+
+:sfile:`examples/cohomology/rips-pairwise-cohomology.py` gives an example of the
+same computation performed in Python (but with the output in a different format).
+
+After the simplicial complex is computed in a list `simplices`, and the list is
+sorted with respect to the Rips filtration order, the simplices are inserted
+into the :class:`CohomologyPersistence` one by one::
+
+ # list simplices is created
+
+ ch = CohomologyPersistence(prime)
+ complex = {}
+
+ for s in simplices:
+ i,d = ch.add([complex[sb] for sb in s.boundary], (s.dimension(), s.data))
+ complex[s] = i
+ if d:
+ dimension, birth = d
+ print dimension, birth, s.data
+ # else birth
+
+Above dictionary `complex` maintains the map of simplices to indices returned by
+:meth:`CohomologyPersistence.add`. The pair `(dimension, data)` is used as the
+birth value. Here `data` is the value associated with the simplex in the Rips
+filtration. The pair is returned back if a death occurs, and is printed on the
+standard output. After the for loop finishes, one may output infinite
+persistence classes with the following for loop::
+
+ for ccl in ch:
+ dimension, birth = ccl.birth
+ if dimension >= skeleton: continue
+ print dimension, birth, 'inf' # dimension, simplex data = birth
+
+Naturally one may iterate over `ccl` which is of type :class:`Cocycle` and
+extract more information. For example, this is necessary to get the coefficients
+that serve as the input for :sfile:`examples/cohomology/cocycle.py`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/examples/index.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,55 @@
+.. _examples:
+
+Examples
+========
+
+The most basic example and therefore a good place to start getting acquainted
+with the library is the :ref:`triangle-example`. It adds simplices of a triangle
+one by one, and then (in case of a :ref:`triangle-zigzag-example`), removes them
+one by one.
+
+.. toctree::
+
+ triangle
+ triangle-zigzag
+
+The simplest example that instead of specifying the complex explicitly,
+constructs it from the input point set is the :ref:`alpha-shape-example`. The
+example reads points from a file, determines their dimension dynamically (based
+on the number of coordinates in the first line of the file), and then constructs
+an alpha shape and outputs its persistence diagram.
+
+.. toctree::
+
+ alphashape
+
+Another example that follows a similar strategy is the computation of the
+Vietoris-Rips complex. Since only pairwise distances are required it works with
+points in arbitrary dimension. (Of course, in dimensions 2 and 3 the complexes
+are much larger than those for the :ref:`alpha-shape-example`).
+
+.. toctree::
+ :maxdepth: 1
+
+ rips
+
+One may use persistent cohomology algorithm to extract persistent cocycles,
+turn them into harmonic cocycles, and use them to parametrize the input point
+set; for details see [dSVJ09]_. The explanation of how to use Dionysus to
+achieve this is available.
+
+.. toctree::
+ :maxdepth: 1
+
+ cohomology
+
+A simple example of computing persistence of a lower-star filtration is in
+:sfile:`examples/pl-functions/lsfiltration.py`.
+
+A C++-only, but useful example is computation of a vineyard of piecewise
+straight-line homotopy of piecewise-linear functions.
+
+.. toctree::
+ :maxdepth: 1
+
+ pl-vineyard
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/examples/pl-vineyard.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,72 @@
+.. _pl-vineyard:
+
+Piecewise-Linear Vineyard
+=========================
+
+Given a simplicial complex :math:`K`, and a sequence of values on each one of
+its vertices, one may construct a homotopy of PL functions on the complex that
+interpolates linearly between the values. For any given time in the homotopy, we
+get a function from the simplicial complex to the real line, and we can compute
+its persistence diagram. Stacking all such diagrams together we get a
+persistence vineyard [CEM06]_. An example that computes such a vineyard is in
+:sfile:`examples/pl-functions/pl-vineyard.cpp`.
+
+.. program:: pl-vineyard
+
+Once compiled, it takes three files as the input::
+
+ pl-vineyard complex values output-prefix
+
+``complex`` lists the simplices of the simplicial complex :math:`K`, one
+per-line::
+
+ 0
+ 1
+ 0 1
+ 2
+ 0 2
+ ...
+
+``values`` lists the vertex values of the consequtive "frames" of the homotopy.
+Each line is a sequence of as many numbers as there are vertices in the complex.
+It describes a single frame. :program:`pl-vineayrd` constructs the homotopy over
+the interval :math:`[0,k-1]`, where :math:`k` is the number of frames. As an
+example of ``values`` input::
+
+ 3.3 6 2
+ 1.2 3 10
+ 7.5 2.1 0
+
+This input means: :math:`f_0(0) = 3.3, f_1(0) = 1.2, f_2(0) = 7.5`. Similarly,
+:math:`f_0(1) = 6, f_1(1) = 3, f_2(1) = 2.1`;
+:math:`f_0(2) = 2, f_1(2) = 10, f_2(2) = 0`.
+
+The vineyard is saved to the files prefixed with ``output-prefix``, followed by
+the dimension and extension, e.g. ``myfunction1.vin`` or ``myfunction1.edg``,
+depending on the format. The two formats are vines and edges. The former saves
+one vine per line, listed as a stream of triplets BIRTH DEATH TIME::
+
+ 4 5 0 3.4 5.6 0.4 3 6 1 ...
+
+The edge format represents the vine as a sequence of edges, each given as a
+start and end point. So the above vine would appear as::
+
+ 4 5 0
+ 3.4 5.6 0.4
+ 3.4 5.6 0.4
+ 3 6 1
+ ...
+
+:program:`pl-vineyard` takes additional options:
+
+.. cmdoption:: -s, --skip-infinite
+
+ Do not output infinite vines.
+
+.. cmdoption:: -v, --save-vines
+
+ Output vines, instead of the default edge format.
+
+.. cmdoption:: -e, --explicit-events
+
+ Go through the events one by one (useful for the debugging).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/examples/rips.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,71 @@
+.. _rips-example:
+
+Rips complex example
+====================
+
+.. todo::
+ Explain `Vietoris-Rips complex`_.
+
+There is an elementary example in :sfile:`examples/rips/rips.py` that computes a
+Rips complex of a few points with integer coordinates on a line. It illustrates
+the use of Rips complexes and in particular of defining your own notion of a
+:ref:`Distance <Distances>` based on which the Rips complex is constructed.
+
+A more useful example is given in :sfile:`examples/rips/rips-pairwise.py` (and
+its C++ counterpart in :sfile:`examples/rips/rips-pairwise.cpp`). The example
+takes on the command line the filename of a file with points in Euclidean space
+(one point per line), and a cut off parameters for the skeleton and the
+:math:`\epsilon` parameter for the Rips complex construction. It then constructs
+the Rips complex up to these cutoff parameters, computes its persistence, and
+outputs the persistence diagram (one point per line).
+
+.. literalinclude:: ../../examples/rips/rips-pairwise.py
+
+The bit that sets up the Rips complex is::
+
+ distances = PairwiseDistances(points)
+ rips = Rips(distances)
+ simplices = Filtration()
+ rips.generate(skeleton, max, simplices.append)
+
+The computation of persistence and output of the persistence diagram are the
+same as in the :ref:`alpha-shape-example`. The example also incorporates
+the :ref:`speed-up-suggestions` given in the :ref:`tutorial`.
+
+
+
+C++ sketch
+----------
+.. highlight:: cpp
+
+.. warning:: This section is not finished.
+
+The example given in :sfile:`examples/rips/rips.cpp` illustrates how one can use
+the library to compute persistence of a `Vietoris-Rips complex`_ for a given set of
+distances. At the top of the file a `struct Distances` is defined. The
+particular distances in the example are trivial (they are points with integer
+coordinates on a real line), however, the `struct` illustrates the basic
+requirements of any such class to be passed to the `Rips<Distances>` class.
+
+.. _`Vietoris-Rips complex`: http://en.wikipedia.org/wiki/Vietoris-Rips_complex
+
+The Rips complex itself is generated in the line::
+
+ rips.generate(2, 50, make_push_back_functor(complex));
+
+which tells it to generate a 2-skeleton of the Rips complex up to
+distance value of 50, and insert the simplices into the previously defined
+vector `complex`.
+
+Subsequent sort is unnecessary since Bron-Kerbosch algorithm that generates the
+complex will actually generate the simplices in lexicographic order; it's there
+for illustration purposes only (the simplices must be sorted
+lexicographically).
+
+The following "paragraph" sets up the filtration with respect to simplex sizes
+(specified by `Generator::Comparison(distances)`), and computes its persistence::
+
+ // Generate filtration with respect to distance and compute its persistence
+ Fltr f(complex.begin(), complex.end(), Generator::Comparison(distances));
+ Persistence p(f);
+ p.pair_simplices();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/examples/triangle-zigzag.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,38 @@
+.. _triangle-zigzag-example:
+
+Triangle zigzag example
+=======================
+
+Simple example of a filtered triangle where simplices are first inserted in a
+given order, and then removed in the reverse order is in
+:sfile:`examples/triangle/triangle-zigzag.cpp`. Its Python equivalent
+(:sfile:`examples/triangle/triangle-zigzag.py`) is described next.
+
+.. literalinclude:: ../../examples/triangle/triangle-zigzag.py
+ :language: python
+
+Unlike the :ref:`triangle-example`, here we use :class:`ZigzagPersistence` to
+compute the pairings, and therefore need to store the internal representations
+of the simplicies used by the class. These representation are stored in the
+dictionary ``complex``, which maps the simplices to their representations for
+:class:`ZigzagPersistence`.
+
+The first for loop processes the simplices sorted with respect to
+:func:`data_cmp`. :meth:`ZigzagPersistence.add` invoked within the loop accepts
+the boundary of the newly added cell in its internal representation, which is
+computed by looking up each simplex in the dictionary ``complex``:
+``[complex[ss] for ss in s.boundary]``. If there is a birth, the value to be
+associated with the newly created class is ``b`` (which in this case is simply a
+counter). :meth:`~ZigzagPersistence.add` returns a pair ``(i,d)``. The former
+is an internal representation of the newly added cell, which we immediately
+record with ``complex[s] = i``. The latter is an indicator of whether a death
+has occurred, which happens iff ``d is not None``, in which case ``d`` is the
+birth value passed to :meth:`~ZigzagPersistence.add` whenever the class that
+just died was born. If the death occurred, then we outut the interval ``(d,
+b-1)``.
+
+The second for loop removes simplices in the reverse order of their insertion.
+:meth:`~ZigzagPersistence.remove` takes the index of the cells to be removed
+(looked up in the ``complex`` dictionary: ``complex[s]``), and takes a birth
+value in case a class is born. It return only a death indicator (which again is
+``None`` if no death occurred).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/examples/triangle.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,37 @@
+.. _triangle-example:
+
+Triangle example
+================
+
+Simple example of a filtered triangle is given in
+:sfile:`examples/triangle/triangle.cpp`. Its equivalent in Python appears in
+:sfile:`examples/triangle/triangle.py`, and we describe it next.
+
+.. literalinclude:: ../../examples/triangle/triangle.py
+ :language: python
+
+After the necessary imports, the ``complex`` is setup explicitly as a list of
+simplices. Each :class:`Simplex` constructor takes an iterable sequence of
+vertices, and optionally a data value.
+
+A filtration ``f`` is initialized using the :class:`Filtration` class, which
+takes a list of simplices (or anything iterable) and a comparison that defines
+in what order the simplices should come in the filtration. In this case we use
+:func:`data_cmp`, which simply compares simplices' :attr:`~Simplex.data`
+attributes.
+
+:class:`StaticPersistence` is initialized with the filtration, and its method
+:meth:`~StaticPersistence.pair_simplices` pairs the simplices of the
+filtration::
+
+ p = StaticPersistence(f)
+ p.pair_simplices()
+
+Subsequently, we iterate over ``p`` to access a representation of each simplex
+in the filtration order. We output each simplex, its sign, and its pair. The auxilliary
+``smap = p.make_simplex_map(f)`` remaps the indices of :class:`StaticPersistence` into
+the simplices in the filtration.
+Naturally, one could use this to access the
+:attr:`~Simplex.data` attribute of the simplices to output the actual
+persistence diagram, as is done in the :ref:`alpha-shape-example` and the
+:ref:`rips-example`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ext/cppdocs.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,202 @@
+from docutils import nodes, utils
+from docutils.parsers.rst import directives
+from sphinx import addnodes
+from sphinx.util.compat import Directive
+
+from sphinx.directives.desc import CDesc
+
+############
+# CppClass #
+############
+class CppClass(nodes.Part, nodes.Element):
+ @staticmethod
+ def html_visit(visitor, node):
+ visitor.body.append('<dl class="class">')
+ visitor.body.append(visitor.starttag(node, 'dt'))
+ if node['tparams']:
+ visitor.body.append('<em class="property">template</em> <')
+ tparam_names = ['class ' + tp[0] for tp in node['tparams']]
+ visitor.body.append(', '.join(tparam_names))
+ visitor.body.append('><br/>')
+
+ visitor.body.append('<em class="property">class</em> ')
+ visitor.body.append('<tt class="descname">' + node['name'] + '</tt>')
+ visitor.body.append(u'<a class="headerlink" href="#%s" title="%s">\u00B6</a>' %
+ ('cppclass-'+node['name'], _('Permalink to this class')))
+ visitor.body.append('</dt>')
+ visitor.body.append('<dd>')
+
+ if node['derives']:
+ visitor.body.append('<p>Derives from ')
+ for d in node['derives'].split(','):
+ dnode ={ 'type': 'cppclass', 'target': d.strip() }
+ Ref.html_visit(visitor, dnode)
+ visitor.body.append(d)
+ Ref.html_depart(visitor, dnode)
+ visitor.body.append('.</p>')
+
+ visitor.body.append('<p>')
+ for name, desc in node['tparams']:
+ visitor.body.append(name + ' — ' + desc + '<br/>')
+ visitor.body.append('</p>')
+
+
+ @staticmethod
+ def html_depart(visitor, node):
+ visitor.body.append('</dd></dl>')
+
+
+class CppClassDirective(Directive):
+ has_content = True
+ required_arguments = 1
+ final_argument_whitespace = True
+ option_spec = { 'derives': directives.unchanged,
+ 'tparam': directives.unchanged }
+
+ def run(self):
+ env = self.state.document.settings.env
+
+ cppclass = CppClass()
+ cppclass['name'] = self.arguments[0]
+ cppclass['derives'] = self.options.get('derives')
+
+ targetname = '%s-%s' % ('cppclass', cppclass['name'])
+ targetnode = nodes.target('', '', ids=[targetname])
+ self.state.document.note_explicit_target(targetnode)
+
+ indextext = _('%s (C++ class)') % cppclass['name']
+ inode = addnodes.index(entries = [('single', indextext,
+ 'cppclass-' + cppclass['name'],
+ cppclass['name'])])
+
+ self.state.nested_parse(self.content, self.content_offset, cppclass)
+
+ return [inode, targetnode, cppclass]
+
+class TParam(nodes.Element):
+ pass
+
+class TParamDirective(Directive):
+ required_arguments = 1
+ optional_arguments = 1
+ final_argument_whitespace = True
+
+ def run(self):
+ tparam = TParam()
+
+ tparam['name'] = self.arguments[0]
+ if len(self.arguments) > 1:
+ tparam['description'] = self.arguments[1]
+
+ return [tparam]
+
+
+#############
+# CppMethod #
+#############
+class CppMethod(nodes.Part, nodes.Element):
+ @staticmethod
+ def html_visit(visitor, node):
+ visitor.body.append(visitor.starttag(node, 'dt'))
+ visitor.body.append(node['name'])
+ visitor.body.append(u'<a class="headerlink" href="#%s" title="%s">\u00B6</a>' %
+ ('cppmethod-' + node['classname'] + '::' + node['name'], _('Permalink to this class')))
+ visitor.body.append('</dt>')
+ visitor.body.append('<dd>')
+
+ @staticmethod
+ def html_depart(visitor, node):
+ visitor.body.append('</dd></dl>')
+
+
+class CppMethodDirective(Directive):
+ has_content = True
+ required_arguments = 1
+ final_argument_whitespace = True
+
+ def run(self):
+ env = self.state.document.settings.env
+
+ cppmethod = CppMethod()
+ cppmethod['name'] = self.arguments[0] # TODO: parse name
+
+ targetname = '%s-%s' % ('cppmethod', cppmethod['name'])
+ targetnode = nodes.target('', '', ids=[targetname])
+ self.state.document.note_explicit_target(targetnode)
+
+ indextext = _('%s (C++ method)') % cppmethod['name']
+ inode = addnodes.index(entries = [('single', indextext,
+ 'cppmethod-' + cppmethod['name'],
+ cppmethod['name'])])
+
+ self.state.nested_parse(self.content, self.content_offset, cppmethod)
+
+ return [inode, targetnode, cppmethod]
+
+
+class Ref(nodes.Inline, nodes.TextElement):
+ @staticmethod
+ def html_visit(visitor, node):
+ if node['type'] == 'cppclass':
+ visitor.body.append('<a href="#%s-%s">' % (node['type'], node['target']))
+ elif node['type'] == 'cppmethod':
+ # TODO: check if the name is not fully qualified, and has a parent CppClass node,
+ # in that case, prepend the name, otherwise
+ visitor.body.append('<a href="#%s-%s::%s">' % (node['type'], node['classname'], node['target']))
+
+
+ @staticmethod
+ def html_depart(visitor, node):
+ visitor.body.append('</a>')
+
+def cppclass_role(role, rawtext ,text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ node = Ref(text, text, target=text, type='cppclass')
+ node['docname'] = inliner.document.settings.env.docname
+ return [node], []
+
+def cppmethod_role(role, rawtext ,text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ node = Ref(text, text, target=text, type='cppmethod')
+ node['docname'] = inliner.document.settings.env.docname
+ return [node], []
+
+
+def process_classnames(app, doctree, fromdocname):
+ for node in doctree.traverse(CppClass):
+ for method in node.traverse(CppMethod):
+ method['classname'] = node['name']
+ for ref in node.traverse(Ref):
+ if ref['type'] == 'cppmethod':
+ ref['classname'] = node['name']
+
+def process_tparams(app, doctree, fromdocname):
+ for node in doctree.traverse(CppClass):
+ node['tparams'] = []
+ for tparam in node.traverse(TParam):
+ node['tparams'].append((tparam['name'], tparam['description']))
+
+
+def process_cfunction_scope(app, doctree, fromdocname):
+ for node in doctree.traverse():
+ if 'ctype' in node: print node
+ if 'cfunction' in node: print node
+
+def setup(app):
+ app.add_node(Ref, html=(Ref.html_visit, Ref.html_depart))
+
+ app.add_directive('cppclass', CppClassDirective)
+ app.add_node(CppClass, html=(CppClass.html_visit, CppClass.html_depart))
+ app.add_role('cppclass', cppclass_role)
+
+ app.add_directive('tparam', TParamDirective)
+ app.add_node(TParam, html=(lambda v,n: '', lambda v,n: ''))
+
+ app.add_directive('cppmethod', CppMethodDirective)
+ app.add_node(CppMethod, html=(CppMethod.html_visit, CppMethod.html_depart))
+ app.add_role('cppmethod', cppmethod_role)
+
+ app.connect('doctree-resolved', process_classnames)
+ app.connect('doctree-resolved', process_tparams)
+
+ app.connect('doctree-resolved', process_cfunction_scope)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ext/sfile.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,14 @@
+from docutils import nodes, utils
+import posixpath
+
+def sfile_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]):
+ env = inliner.document.settings.env
+ baseuri = env.config.sfile_base_uri
+ text = utils.unescape(etext)
+ refnode = nodes.reference('', '', refuri=posixpath.join(baseuri, text))
+ refnode += nodes.literal(text, text)
+ return [refnode], []
+
+def setup(app):
+ app.add_role('sfile', sfile_role)
+ app.add_config_value('sfile_base_uri', 'http://example.com/source', True)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ext/sprite_jsmath.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,2 @@
+def setup(app):
+ app.add_javascript('jsMath/plugins/spriteImageFonts.js')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/get-build-install.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,135 @@
+.. _download:
+
+Get, Build, Install
+===================
+
+The most up to date code is available from
+`my Mercurial repository`_.
+If you have Mercurial_, the easiest way to obtain the code is by cloning it:
+
+.. parsed-literal::
+
+ hg clone |dionysus-url|
+ cd Dionysus
+ hg up tip
+
+If you don't have time or desire to deal with Mercurial, you can download the
+`tarball of the entire repository`_. The advantage of using Mercurial is that it
+makes it very easy to keep up with the updates that are periodically committed
+to the repository::
+
+ hg pull -u
+
+
+.. |dionysus-url| replace:: http://hg.mrzv.org/Dionysus/
+
+.. _Mercurial: http://www.selenic.com/mercurial/
+
+.. _`tarball of the entire repository`: http://hg.mrzv.org/Dionysus/archive/tip.tar.gz
+.. _`my Mercurial repository`: http://hg.mrzv.org/Dionysus/
+
+
+Dependencies
+------------
+Dionysus requires the following software:
+
+ :CMake_: for building (version :math:`\geq` 2.6)
+ :Boost_: C++ utilities (version :math:`\geq` 1.36; including Boost.Python used to create
+ Python bindings)
+
+Optional dependencies:
+
+ :CGAL_: for alpha shapes (version :math:`\geq` 3.4)
+ :CVXOPT_: for :ref:`circle-valued parametrization <cohomology-parametrization>` using LSQR
+ :PyQt4_: for :mod:`viewer` module
+ :PyOpenGL_, NumPy_: for 3D visualization in :mod:`viewer` module
+ :PyX_: :sfile:`tools/draw-diagram/draw.py` uses `PyX`_ to
+ produce a PDF of the diagram
+ :rlog_: used for logging only (not needed by default)
+
+.. :dsrpdb_: for reading PDB files
+ :SYNAPS_: for solving polynomials (for kinetic kernel), which in
+ turn requires GMP_
+
+.. _CMake: http://www.cmake.org
+.. _Boost: http://www.boost.org
+.. _CGAL: http://www.cgal.org
+.. _CVXOPT: http://abel.ee.ucla.edu/cvxopt/
+.. _PyQt4: http://www.riverbankcomputing.co.uk/software/pyqt/intro
+.. _PyOpenGL: http://pyopengl.sourceforge.net/
+.. _NumPy: http://numpy.scipy.org/
+.. _PyX: http://pyx.sourceforge.net/
+.. _rlog: http://www.arg0.net/rlog
+.. _dsrpdb: http://www.salilab.org/~drussel/pdb/
+.. _SYNAPS: http://www-sop.inria.fr/galaad/synaps/
+.. _GMP: http://gmplib.org/
+
+
+Building
+--------
+To build the examples as well as the :ref:`Python bindings <python-bindings>`,
+create a directory ``build``. Inside that directory run ``cmake`` and ``make``::
+
+ mkdir build
+ cd build
+ cmake ..
+ make
+
+.. tip::
+
+ To use GCC 4.2 on a Mac one can try ``CXX=g++-4.2 cmake ..`` instead of
+ ``cmake ..``.
+
+Instead of ``cmake``, one can run ``ccmake`` for a curses interface. The
+following configuration options are available. One can set them either through
+the curses interface or by passing a flag of the form ``-Doptimize:bool=on`` to
+``cmake``.
+
+ :debug: Turns on debugging compilation
+ :optimize: Turns on compiler optimizations (`on` by default)
+ :logging: Turns on logging facilities
+ :counters: Turns on various built-in counters
+
+Depending on the combination of debugging and optimization, a particular
+``CMAKE_CXX_FLAGS*`` is chosen.
+
+.. tip:: The default settings work fine unless you want to dive into the
+ library's internals with logging or study the performance of various
+ algorithms with counters.
+
+.. todo:: Write sections on logging and counters.
+
+Some parts of Dionysus understand the ``DEBUG_CONTAINERS`` definition which can
+be appended to ``CMAKE_CXX_FLAGS``. If set, the library will use GCC STL's
+debugging containers (from the ``std::__debug`` namespace defined in ``debug/*``
+header files). These containers return safe iterators (the kind that check
+whether they are singular when compared, or additionally whether they are out of
+bounds when dereferenced).
+
+.. todo:: ``ZIGZAG_CONSISTENCY`` definition
+
+
+Install
+-------
+
+At the moment there are no installation procedures. To run the Python code you
+need to have ``.../build/bindings/python`` somewhere in your ``PYTHONPATH``.
+I.e. add::
+
+ export PYTHONPATH=.../build/bindings/python
+
+to your ``~/.bashrc`` (assuming you are using Bash_). Alternatively, run the
+python examples from within ``.../build/bindings/python``::
+
+ python .../Dionysus/examples/triangle/triangle.py
+
+The C++ examples can be run from anywhere. The C++ library consists only of
+header files (no library actually needs to be built), so to compile against it,
+it suffices to add ``-I .../Dionysus/include`` to your ``g++`` flags::
+
+ g++ your-code.cpp -o your-code -I .../Dionysus/include
+
+Proper installation procedures (with ``make install``) will be added in the
+future.
+
+.. _Bash: http://www.gnu.org/software/bash/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/index.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,60 @@
+Welcome to Dionysus' documentation!
+===================================
+
+Dionysus is a C++ library for computing persistent homology. It provides
+implementations of the following algorithms:
+
+.. sidebar:: Contents
+
+ .. toctree::
+ :maxdepth: 1
+
+ get-build-install
+ tutorial
+ examples/index
+ python/overview
+ bibliography
+
+* Persistent homology computation [ELZ02]_ [ZC05]_
+* Vineyards [CEM06]_ |cpp-only|
+* Persistent cohomology computation (described in [dSVJ09]_)
+* Zigzag persistent homology [CdSM09]_
+* :ref:`examples` provide useful functionality in and of themselves:
+
+ * :ref:`Alpha shape construction <alpha-shape-example>` in 2D and 3D
+ * :ref:`Rips complex construction <rips-example>`
+ * Cech complex construction |cpp-only|
+ * :ref:`Circle-valued parametrization <cohomology-parametrization>`
+ * :ref:`Piecewise-linear vineyards <pl-vineyard>`
+
+.. todo::
+ Document more examples.
+
+The C++ API is currently very poorly documented. One's best source for its
+documentation is its usage in various :ref:`examples` (located in
+:sfile:`examples/`).
+
+The :ref:`Python bindings <python-bindings>` provide both a simple interface to
+the low-level C++ functionality as well as high-level auxilliary routines. Their
+"thinness" is meant to provide the efficiency benefits of C++ together with the
+simplicity, elegance, and interactivity of Python. Since they mimick the C++
+functionality, their documentation may be a helpful resource for the latter.
+
+:ref:`Download <download>` the software, or read a :ref:`tutorial` to
+get acquainted with its structure.
+
+The library is distributed under the GPL_ license.
+
+.. _GPL: http://www.gnu.org/licenses/gpl.html
+
+.. include:: substitutions.aux
+
+
+..
+ Indices and tables
+ ==================
+
+ * :ref:`genindex`
+ * :ref:`modindex`
+ * :ref:`search`
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/alphashapes.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,47 @@
+.. _alphashapes:
+
+Alpha shapes
+============
+
+There are two functions provided to compute alpha shapes. One in 2D and one in
+3D. Both take a list of points (each a list of coordinates) as input, and fill a
+list with the simplices of the `Delaunay triangulation`_. Each such simplex is
+said to be *attached* (or *regular*) if its dual Voronoi cell does not contain a
+critical point of the distance function to the point set. The smallest value of
+the squared distance function on the dual Voronoi cell of the Delaunay simplex
+is the alpha shape value assigned to it. This value is stored in the simplex's
+`data` attribute; whether it is attached is stored in the `attached` attribute.
+
+.. _`Delaunay triangulation`: http://en.wikipedia.org/wiki/Delaunay_triangulation
+
+
+.. function:: fill_alpha_complex(points, complex)
+
+ Based on the dimension of the first point, decides which of the two functions
+ below to call.
+
+.. function:: fill_alpha2D_complex(points, complex)
+
+ Appends to the `complex` the simplices of the 2D Delaunay triangulation
+ on the `points`.
+
+.. function:: fill_alpha3D_complex(points, complex)
+
+ Appends to the `complex` the simplices of the 3D Delaunay triangulation
+ on the `points`.
+
+
+Example
+-------
+
+The following example generates 10 points on a circle, and computes their
+Delaunay triangulation with corresponding alpha shape values::
+
+ from math import sin, cos, pi
+ points = [[cos(2*pi*t/10), sin(2*pi*t/10)] for t in xrange(10)]
+ complex = Filtration()
+ fill_alpha2D_complex(points, complex)
+
+One can extract any given alpha shape with the usual Python list notation::
+
+ alphashape = [s for s in complex if s.data[0] <= .5]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/cohomology-persistence.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,175 @@
+:class:`CohomologyPersistence` class
+====================================
+
+The :ref:`rips-pairwise-cohomology` illustrates the use of :class:`CohomologyPersistence`.
+
+.. class:: CohomologyPersistence
+
+ .. method:: __init__(prime = 11)
+
+ Initializes :class:`CohomologyPersistence` with the given `prime`; from
+ this point on all the computation will be performed with coefficients
+ in :math:`\mathbb{Z}/prime \mathbb{Z}`.
+
+ .. method:: add(boundary, birth, [store = True], [image = True], [coefficients = []])
+
+ Adds a simplex with the given `boundary` to the complex, i.e.
+ :math:`K_{i+1} = K_i \cup \sigma` and `boundary` = :math:`\partial \sigma`.
+ If a new class is born as a result of the addition, `birth` is stored with
+ it for future reference.
+
+ If `store` is ``False`` and a class is born, it will not be stored in
+ :class:`CohomologyPersistence`. This avoids wasting space on the
+ classes of the dimension equal to the maximum-dimensional simplices of
+ the complex since such classes will never die.
+
+ The `image` parameter allows one to work with a case of a space
+ :math:`L \subseteq K` where the filtration of :math:`K` induces a
+ filtration of :math:`L`. In this case, one may want to compute **image
+ persistence** (i.e. the persistence of the sequences of the images given
+ by the inclusion of :math:`L` in :math:`K`). `image` indicates whether
+ the simplex added belongs to :math:`L` or not.
+
+ If given, `coefficients` is a list parallel to `boundary` that provides
+ coefficients for the corresponding boundary elements. If empty, it is
+ assumed to be :math:`(-1)^i`.
+
+ :returns: a triple (`i`, `d`, `ccl`). The first element is the index `i`.
+ It is the internal representation of the newly added simplex,
+ and should be used later when constructing the
+ boundaries of its cofaces. In other words, `boundary` must
+ consist of these indices. The second element `d` is the death
+ element. It is `None` if a birth occurred, otherwise it
+ contains the value passed as `birth` to
+ :meth:`~CohomologyPersistence.add` when the class that just
+ died was born.
+ The third element `ccl` returns the dying cocycle
+ (iterable over instances of :class:`CHSNode`), in case of a death.
+ It's empty if a birth occurs.
+
+ .. method:: __iter__()
+
+ Iterator over the live cocycles stored in
+ :class:`CohomologyPersistence`. The returned elements are of the type
+ :class:`Cocycle` below.
+
+
+.. class:: Cocycle
+
+ .. attribute:: birth
+
+ The birth value associated with the cocycle. It is passed to
+ :class:`CohomologyPersistence` in method
+ :meth:`~CohomologyPersistence.add`.
+
+ .. method:: __iter__()
+
+ Iterator over the individual nodes (simplices) of the cocycle, each of type
+ :class:`CHSNode`.
+
+.. class:: CHSNode
+
+ .. attribute:: si
+
+ The index of the simplex, of type :class:`CHSimplexIndex`.
+
+ .. attribute:: coefficient
+
+ Coefficient in :math:`\mathbb{Z}/prime \mathbb{Z}` associated with the
+ simplex.
+
+
+.. class:: CHSimplexIndex
+
+ .. attribute:: order
+
+ The count associated with the simplex when it is inserted into
+ :class:`CohomologyPersistence`.
+
+
+Adaptor
+-------
+
+:class:`StaticCohomologyPersistence` provides a wrapper around
+class :class:`CohomologyPersistence` that's compatible with :class:`StaticPersistence`.
+See the documentation of the latter class for details.
+
+
+.. class:: StaticCohomologyPersistence
+
+ .. method:: __init__(filtration, prime = 2)
+
+ Initializes :class:`StaticCohomologyPersistence` with the given
+ :class:`Filtration`. `prime` is passed straight to the wrapped
+ :class:`CohomologyPersistence` class.
+
+ .. method:: pair_simplices()
+
+ Pairs simplices using :class:`CohomologyPersistence` class.
+
+ .. method:: __call__(i)
+
+ Given a node in the internal representation, the method returns its
+ integer offset from the beginning of the filtration.
+
+ .. method:: make_simplex_map(filtration)
+
+ Creates an auxilliary map from the nodes to the simplices::
+
+ smap = persistence.make_simplex_map(filtration)
+ for i in persistence:
+ if i.unpaired(): print smap[i]
+
+ .. method:: __iter__()
+
+ Iterator over the nodes (representing individual simplices). See
+ :class:`APNode`.
+
+ .. method:: __len__()
+
+ Returns the number of nodes (i.e. the number of simplices).
+
+.. class:: APNode
+
+ The following methods behave the same way as they do in :class:`SPNode`.
+
+ .. method:: sign()
+
+ .. method:: pair()
+
+ .. method:: unpaired()
+
+ The only crucial distinction in the behavior comes with the attribute
+ :attr:`cocycle`.
+
+ .. attribute:: cocycle
+
+ If the simplex is positive, this attribute stores a cocycle it created (recorded at the time of its death).
+ The 1-dimensional cocycles can be used with the :func:`circular.smooth` function to turn
+ them into circle-valued maps.
+ ::
+
+ for i in persistence:
+ if i.sign(): print i.cocycle
+
+
+.. class:: ImagePersistence
+
+ This class is another wrapper around :class:`CohomologyPersistence` that can
+ compute image persistence induced by inclusion of a subcomplex. Its
+ interface is the same as :class:`StaticCohomologyPersistence` above, except
+ for the constructor:
+
+ .. method:: __init__(filtration, subcomplex)
+
+ `subcomplex` is a function called with every simplex. It should return
+ ``True`` if the simplex belong to the subcomplex; ``False`` otherwise.
+
+
+Circular coordinates
+--------------------
+
+.. function:: circular.smooth(filtration, cocycle)
+
+ Returns a map from the vertices of the simplicial complex `filtration` to a circle :math:`[-.5, .5]`,
+ where the opposite ends of the interval are identified.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/filtration.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,43 @@
+:class:`Filtration` class
+=========================
+
+.. class:: Filtration
+
+ This class serves as a representation of the simplicial complex. It knows both
+ how to perform a fast lookup of a given simplex, as well as how to
+ iterate over the simplices in a sorted order.
+
+ .. method:: __init__()
+ .. method:: __init__(simplices, cmp)
+
+ Initializes :class:`Filtration` by internally storing the elements of the sequence
+ `simplices`, and in the order sorted with respect to `cmp`.
+
+ .. method:: append(s)
+
+ Appends the given simplex `s` to the filtration.
+
+ .. method:: sort(cmp)
+
+ Sorts the filtration with respect to the comparison `cmp`.
+
+ .. method:: __getitem__(i)
+
+ Random access to the elements of the filtration.
+
+ .. method:: __call__(s)
+
+ Finds the integer index of the given simplex in the sorted order of the filtration.
+
+ .. method:: __iter__()
+
+ Iterator over the elements of the filtration sorted with respect
+ to the comparison `cmp`. E.g.::
+
+ simplices = [Simplex([0], 2), ..., Simplex([3,4,5], 3.5)]
+ f = Filtration(simplices, data_dim_cmp)
+ for s in f: print s
+
+ .. method:: __len__()
+
+ Size of the filtration.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/overview.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,24 @@
+.. _python-bindings:
+
+Python bindings: module :mod:`dionysus`
+=======================================
+
+.. module:: dionysus
+.. moduleauthor:: Dmitriy Morozov <dmitriy@mrzv.org>
+
+The :ref:`tutorial` describes how to use the bindings. The pages in this section
+document the API of various classes and functions.
+
+The following classes are available in the module:
+
+.. toctree::
+ :maxdepth: 1
+
+ simplex.rst
+ filtration.rst
+ static-persistence.rst
+ cohomology-persistence.rst
+ alphashapes.rst
+ rips.rst
+ zigzag-persistence.rst
+ persistence-diagram.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/persistence-diagram.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,67 @@
+:class:`PersistenceDiagram` class
+==================================
+
+.. class:: PersistenceDiagram
+
+ .. method:: __init__( dimension )
+
+ Initializes : an empty( no points ) :class:`PersistenceDiagram` object and sets
+ the :attr:`~PersistenceDiagram.dimension` attribute( must be integer ) e.g.::
+
+ dia = PersistenceDiagram( 1 )
+
+ .. method:: __init__( dimension, point_seq )
+
+ Initializes :class:`PersistenceDiagram` of specified dimension from the given sequence `seq` of tuples, e.g.::
+
+ dia = PersistenceDiagram( 1, (1,2) )
+
+ The tuples must have at least 2 elements ``(birth, death)``.
+ If there is a third element, it is stored as extra data associated to
+ a point.
+
+ .. method:: append( p )
+
+ Adds point `p` to the persistence diagram.
+
+ .. attribute:: dimension
+
+ Dimension of the persistence diagram. Must be an integer. Must be set at initialization.
+
+ .. method:: __iter__( )
+
+ Iterator over the points in the persistence diagram,
+ e.g.::
+
+ for p in dia: print p
+
+ .. method:: __len__( )
+
+ :returns: The number of points in the diagram.
+
+
+
+Utility functions for persistence diagrams
+--------------------------------------------
+
+.. function:: init_diagrams(persistence, filtration[, eval = lambda s: s.data[, data = lambda i: None]])
+
+ Initializes a collection of :class:`PersistenceDiagram` instances from `persistence`
+ and `filtration`. Optional `eval` can determine how to extract birth and
+ death values from a simplex. For example, if `filtration` was filled using
+ :func:`fill_alpha_complex()`, the :attr:`~Simplex.data` contains a pair ``(value, critical)``.
+ We can extract the ``value`` from the tuple::
+
+ init_diagrams(persistence, filtration, lambda s: s.data[0])
+
+ Optional `data` argument can return arbitrary data to associate with each point,
+ given an node of `persistence`.
+
+.. function:: bottleneck_distance(dia1, dia2)
+
+ Calculates the bottleneck distance between the two persistence diagrams.
+
+.. function:: wasserstein_distance(dia1, dia2, p)
+
+ Calculates the `p`-th Wasserstein distance between the two persistence diagrams.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/rips.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,114 @@
+:class:`Rips` class
+======================
+
+.. class:: Rips
+
+ .. method:: __init__(distances)
+
+ Initializes :class:`Rips` with the given `distances` whose main purpose
+ is to return the distance of two points given their indices. See
+ Distances_ below.
+
+ .. method:: generate(k, max, functor[, seq])
+
+ Calls `functor` with every simplex in the `k`-skeleton of the Rips
+ complex :math:`VR` (`max`). If `seq` is provided, then the complex is
+ restricted to the vertex indices in the sequence.
+
+ .. method:: vertex_cofaces(v, k, max, functor[, seq])
+
+ Calls `functor` with every coface of the vertex `v` in the `k`-skeleton
+ of the Rips complex :math:`VR` (`max`). If `seq` is provided, then the
+ complex is restricted to the vertex indices in the sequence.
+
+ .. method:: edge_cofaces(u, v, k, max, functor[, seq])
+
+ Calls `functor` with every coface of the edge (`u`, `v`) in the
+ `k`-skeleton of the Rips complex :math:`VR` (`max`). If `seq` is
+ provided, then the complex is restricted to the vertex indices in the
+ sequence.
+
+ .. method:: cmp(s1, s2)
+
+ Compares simplices `s1` and `s2` with respect to their ordering in the
+ Rips complex. Note that like Python's built in `cmp` this is a three
+ possible outsome comparison (-1,0,1) for (:math:`\leq, =, \geq`,
+ respectively).
+
+ .. method:: eval(s)
+
+ Returns the size of simplex `s`, i.e. the length of its longest edge.
+
+
+.. _distances:
+
+Distances
+---------
+
+An instance of `distances` passed to the constructor of :class:`Rips` should
+know its length and the distances between the points. The length should be
+retrievable via ``len(distance)`` and it determines how many points the complex
+is built on. The distances between the points are inferred by the class
+:class:`Rips` by calling `distances` with a pair of vertices as arguments.
+
+For example, the following class represents 10 points on an integer lattice::
+
+ class Distances:
+ def __len__(self):
+ return 10
+
+ def __call__(self, x, y):
+ return math.fabs(y-x)
+
+The bindings expose a C++ class as a Python class :class:`PairwiseDistances` to deal with
+explicit points in a Euclidean space. In pure Python it could be defined as
+follows (in fact it used to be a pure Python class, and one may still find it in
+:sfile:`bindings/python/dionysus/distances.py`; its performance is much slower
+than its pure C++ analog)::
+
+ class PairwiseDistances:
+ def __init__(self, points, norm = l2):
+ self.points = points
+ self.norm = norm
+
+ def __len__(self):
+ return len(self.points)
+
+ def __call__(self, p1, p2):
+ return self.norm([x - y for (x,y) in zip(self.points[p1], self.points[p2])])
+
+Another distances class is available that speeds up the computation of the Rips
+complex at the expense of the memory usage: :class:`ExplicitDistances`. It is
+initialized with an instance of any class that behaves like a distances class,
+and it stores all of its distances explicitly to not have to recompute them in
+the future::
+
+ distances = PairwiseDistances(points)
+ distances = ExplicitDistances(distances)
+
+With :class:`PairwiseDistances` being a C++ class, and
+:class:`ExplicitDistances` being pure Python, the speed-up seems minor.
+
+
+Example
+-------
+
+The following example reads in points from a file, and fills the list
+`simplices` with the simplices of the 2-skeleton of the Rips complex built on
+those vertices with distance cutoff parameter 50. Subsequently it computes the
+persistence of the resulting filtration (defined by ``rips.cmp``)::
+
+ points = [for p in points_file('...')]
+ distances = PairwiseDistances(points)
+ rips = Rips(distances)
+ simplices = Filtration()
+ rips.generate(2, 50, simplices.append)
+
+ simplices.sort(rips.cmp)
+ p = StaticPersistence(simplices)
+ p.pair_simplices()
+
+Essentially the same example is implemented in
+:sfile:`examples/rips/rips-pairwise.py`, although it reads the `k` and `max`
+parameters for the Rips complex on the command line, and uses a trick to speed
+up the computation.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/simplex.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,84 @@
+:class:`Simplex` class
+======================
+
+.. class:: Simplex
+
+ .. method:: __init__(seq[, data])
+
+ Initializes :class:`Simplex` from the given sequence `seq` and
+ optionally real value `data`, e.g.::
+
+ s = Simplex([1,2,3], 7.8)
+
+ .. method:: add(v)
+
+ Adds vertex `v` to the simplex, increasing its dimension by 1.
+
+ .. seealso:: :meth:`~Simplex.join`
+
+ .. attribute:: boundary
+
+ Iterator over the boundary :math:`\partial \sigma` of the simplex,
+ e.g.::
+
+ for sb in s.boundary: print sb
+
+ .. method:: contains(v)
+
+ :returns: `True` iff the simplex contains vertex `v`.
+
+ .. method:: dimension()
+
+ :returns: the dimension of the simplex (one less than its number of
+ vertices).
+
+ .. method:: join(other)
+
+ Joins the current simplex with the `other` simplex. The method copies over
+ the vertices from the `other` simplex.
+
+ .. attribute:: data
+
+ Real value stored in the simplex.
+
+ .. attribute:: vertices
+
+ (Sorted) vertices of the simplex accessible as a sequence, e.g.::
+
+ for v in s.vertices: print v,
+
+ .. method:: __hash__()
+ .. method:: __eq__(other)
+
+ Simplices are hashable, and equality comparable, and therefore can be
+ stored in a dictionary. Simplices are equal if their
+ :attr:`~Simplex.vertices` are the same.
+
+
+Utility functions for manipulating simplices
+--------------------------------------------
+
+The first function :func:`vertex_cmp` is a Python interface to a C++ function.
+The rest are pure Python functions defined in
+:sfile:`bindings/python/dionysus/__init__.py`.
+
+.. function:: vertex_cmp(s1, s2)
+
+ Compares the two simplices with respect to the lexicographic order of their vertices.
+
+.. function:: vertex_dim_cmp(s1, s2)
+
+ Compares the two simplices with respect to their dimension, and lexicographically
+ within the same dimension.
+
+.. function:: data_cmp(s1, s2)
+
+ Compares the two simplices with respect to the data (real values) they
+ store.
+
+.. function:: data_dim_cmp(s1, s2)
+
+ Compares the two simplices with respect to their dimension and within the same
+ dimension with respect to their data.
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/static-persistence.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,113 @@
+:class:`StaticPersistence` class
+================================
+
+.. class:: StaticPersistence
+
+ .. method:: __init__(filtration)
+
+ Initializes :class:`StaticPersistence` with the given
+ :class:`Filtration`. This operation effectively computes the boundary
+ matrix of the complex captured by the filtration with rows and columns
+ sorted with respect to the filtration ordering.
+
+ .. method:: pair_simplices(store_negative = False)
+
+ Pairs simplices using the [ELZ02]_ algorithm. `store_negative` indicates
+ whether to store the negative simplices in the cycles.
+
+ .. method:: __call__(i)
+
+ Given an SPNode in the internal representation, the method returns its
+ integer offset from the beginning of the filtration. This is useful to
+ lookup the actual name of the simplex in the complex.
+
+ .. method:: make_simplex_map(filtration)
+
+ Creates an auxilliary :class:`PersistenceSimplexMap` used to lookup the actual
+ simplices from the persistence indices. For example, the following
+ snippet prints out all the unpaired simplices::
+
+ smap = persistence.make_simplex_map(filtration)
+ for i in persistence:
+ if i.unpaired(): print smap[i]
+
+ .. method:: __iter__()
+
+ Iterator over the nodes (representing individual simplices). See
+ :class:`SPNode`.
+
+ .. method:: __len__()
+
+ Returns the number of nodes (i.e. the number of simplices).
+
+
+.. class:: SPNode
+
+ The class represents nodes stored in :class:`StaticPersistence`. These nodes
+ are aware of their :meth:`sign` and :attr:`pair` (and :meth:`cycle` if
+ negative after :meth:`StaticPersistence.pair_simplices` has run).
+
+ .. method:: sign()
+
+ Returns the sign of the simplex: `True` for positive, `False` for
+ negative.
+
+ .. method:: pair()
+
+ Simplex's pair. The pair is set to self if the siplex is unpaired.
+
+ .. attribute:: cycle
+
+ If the simplex is negative, its cycle (that it kills) is non-empty, and
+ can be accessed using this method. The cycle itself is an iterable
+ container of :class:`SPNode`. For example, one can print the basis for
+ the (bounding) cycles::
+
+ smap = persistence.make_simplex_map(filtration)
+ for i in persistence:
+ for ii in i.cycle: print smap[ii]
+
+ .. method:: unpaired()
+
+ Indicates whether the simplex is unpaired.
+
+.. class:: SPersistenceSimplexMap
+
+ .. method:: __getitem__(i)
+
+ Given a persistence index, i.e. an :class:`SPNode`, returns the
+ :class:`Simplex` it represents.
+
+
+:class:`DynamicPersistenceChains` class
+=======================================
+
+.. class:: DynamicPersistenceChains
+
+ This class works exactly like :class:`StaticPersistence`, providing all the
+ same methods. The only difference is that when iterating over it, the
+ elements are of type :class:`DPCNode`, described below.
+
+.. class:: DPCNode
+
+ This class works just like :class:`SPNode`, except it has an additional
+ attribute :attr:`chain`.
+
+ .. attribute:: chain
+
+ It allows one to retrieve the "chain" associated with the simplex.
+ (In terms of the :math:`R = DV` decomposition, it gives access to the
+ columns of the matrix :math:`V`.) In case of the positive simplex, this
+ is a cycle created by the addition of this simplex. This access is
+ particularly useful for the unpaired positive simplices, allowing one to
+ recover the cycles they create. In case of the negative simplex, this chain's
+ boundary is exactly what's stored in the :attr:`~SPNode.cycle` attribute.
+
+ For example, to print out all the essential cycles of the complex, one
+ can run the following loop::
+
+ smap = persistence.make_simplex_map(filtration)
+ for i in persistence:
+ if i.unpaired()
+ for ii in i.chain: print smap[ii]
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/python/zigzag-persistence.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,115 @@
+:class:`ZigzagPersistence` class
+================================
+
+The class deals with the setting :math:`K_1 \rightarrow K_2 \leftarrow K_3 \rightarrow \dots`.
+The :ref:`triangle-zigzag-example` illustrates the use of :class:`ZigzagPersistence`.
+
+.. class:: ZigzagPersistence
+
+ .. method:: add(boundary, birth)
+
+ Adds a simplex with the given `boundary` to the complex, i.e.
+ :math:`K_{i+1} = K_i \cup \sigma` and `boundary` = :math:`\partial \sigma`.
+ If a new class is born as a result of the addition, `birth` is stored with
+ it for future reference.
+
+ :returns: a pair (`i`, `d`). The first element is the index `i`.
+ It is the internal representation of the newly added simplex,
+ and should be used later for removal or when constructing the
+ boundaries of its cofaces. In other words, `boundary` must
+ consist of these indices. The second element `d` is the death
+ element. It is `None` if a birth occurred, otherwise it
+ contains the value passed as `birth` to
+ :meth:`~ZigzagPersistence.add` or
+ :meth:`~ZigzagPersistence.remove` when the class that just
+ died was born.
+
+ .. method:: remove(index, birth)
+
+ Removes the simplex identified by the given `index` from the complex. If
+ a new class is born as a result of the removal, `birth` is stored with
+ it for future reference.
+
+ :returns: `None` if a birth has occurred, otherwise it contains the value
+ passed as `birth` to :meth:`~ZigzagPersistence.add` or
+ :meth:`~ZigzagPersistence.remove` when the class that just
+ died was born.
+
+ .. method:: is_alive(z)
+
+ Determines whether a given cycle is alive. The input should be an
+ instance of :class:`ZNode`.
+
+ .. method:: __iter__()
+
+ Iterator over elements of type :class:`ZNode`, i.e. the cycles stored in the structure.
+
+.. class:: ZNode
+
+ .. attribute:: birth
+
+ The birth value associated with the cycle. It is passed to
+ :class:`ZigzagPersistence` in method
+ :meth:`~ZigzagPersistence.add`.
+
+ .. method:: __iter__()
+
+ Iterator over the individual nodes (simplices) of the cycle (same
+ indices that are passed to and returned by
+ :meth:`~ZigzagPersistence.add`.
+
+
+Auxilliary functions
+--------------------
+
+A pair of auxilliary functions is provided to help add and remove entire
+collections of simplices. Both are pure Python functions defined in
+:sfile:`bindings/python/dionysus/zigzag.py`.
+
+ .. function:: add_simplices(zigzag, simplices, complex, birth, report_local = False)
+
+ Adds each simplex in `simplices` to the `zigzag`. `complex` is a
+ dictionary mapping simplices to their indices (in `zigzag`'s internal
+ representation). All the newly born classes are given the value of
+ `birth`.
+
+ :returns: list of deaths that occur as a result of `simplices`' removal.
+ Each death is a pair of the dimension of the class and the
+ `birth` value passed when the class was born. By default the
+ deaths equal to `birth` are not reported unless `report_local`
+ is set to `True`.
+
+ .. function:: remove_simplices(zigzag, simplices, complex, birth, report_local = False)
+
+ Same parameters and return as in :func:`add_simplices` except that
+ `simplices` are removed from the `zigzag` and the `complex`.
+
+
+
+:class:`ImageZigzagPersistence` class
+=====================================
+
+The class deals with the setting
+
+.. math::
+ \begin{array}{ccccccc}
+ K_1 & \rightarrow & K_2 & \leftarrow & K_3 & \rightarrow & \dots \\
+ \uparrow & & \uparrow & & \uparrow & \\
+ L_1 & \rightarrow & L_2 & \leftarrow & L_3 & \rightarrow & \dots
+ \end{array}
+
+where the vertical maps are inclusions, i.e. :math:`L_i \subseteq K_i`.
+
+.. class:: ImageZigzagPersistence
+
+ .. method:: add(boundary, subcomplex, birth)
+
+ Interface is the same as in :meth:`ZigzagPersistence.add`. The
+ additional parameter `subcomplex` controls whether the simplex is added
+ to :math:`L` or not. We always have :math:`K_{i+1} = K_i \cup \sigma`.
+ If `subcomplex` is true, then :math:`L_{i+1} = L_i \cup \sigma`,
+ otherwise :math:`L_{i+1} = L_i`.
+
+ .. method:: remove(index, birth)
+
+ Interface is exactly the same as in :meth:`ZigzagPersistence.remove`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/substitutions.aux Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,2 @@
+.. |cpp-only| replace:: :sup:`(C++ only)`
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tutorial.rst Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,203 @@
+.. _tutorial:
+
+Brief Tutorial
+==============
+
+It suffices to import only the necessary commands from module :mod:`dionysus`,
+but for the purposes of the tutorial, we import everything::
+
+ from dionysus import *
+
+Read in a points file (for :func:`points_file` the points should appear one per
+line with coordinates separated with spaces; of course, one can read in the
+points however one wants)::
+
+ points = [p for p in points_file('points_filename')]
+ print "Number of points:", len(points)
+
+
+Complexes
+---------
+
+If the points are in :math:`\mathbb{R}^2` or :math:`\mathbb{R}^3`, one can
+construct an alphashape filtration::
+
+ simplices = Filtration()
+ fill_alpha2D_complex(points, simplices) # for 2D, or
+ fill_alpha3D_complex(points, simplices) # for 3D
+
+
+Functions :ref:`fill_alpha*_complex <alphashapes>` fill the ``simplices``
+with all the :class:`simplices <Simplex>` of the Delaunay triangulation.
+Each one has its attribute ``data`` set to a pair: the
+smallest value of the squared distance function on the dual Voronoi cell and
+whether the simplex is critical or not (i.e. whether its dual cell does or
+does not contain a critical point of the distance function).
+See :ref:`alphashapes` for more details, and :ref:`alpha-shape-example` for a
+full example.
+
+As a result, if one wanted only those simplices whose alpha shape value did not
+exceed 10, one could obtain them as follows::
+
+ simplices10 = [s for s in simplices if s.data[0] <= 10]
+
+If the point set lies in higher dimensions, one may construct a Rips complex on
+it. This complex requires only pairwise distances, which makes it very
+versatile. One must first construct an instance of a class providing such
+distances (e.g. :class:`PairwiseDistances` for explicit points, see
+:ref:`distances` for more details), and then pass it to the :class:`Rips`
+complex class::
+
+ distances = PairwiseDistances(points)
+ rips = Rips(distances)
+
+Usually, because of space restrictions, generation of a Rips complex has to be
+restricted to a :math:`k`-skeleton of the complex and some maximal parameter
+:math:`max`. In the following example :math:`k = 3` and :math:`max = 50`::
+
+ simplices = Filtration()
+ rips.generate(3, 50, simplices.append)
+
+:meth:`Rips.generate` takes a skeleton and a maximum distance cutoffs, and a
+function which is called with each of the simplices in the complex (in this
+case, Python list `simplices`' ``append`` method).
+
+
+Persistence
+-----------
+
+There are two ways of computing persistence in Dionysus. The first *offline*
+way, required by :class:`StaticPersistence` (and its derivatives), is to set up
+the entire filtration at once, compute its persistence in one operation, and
+then examine the pairings. The second way is to feed simplices one by one in an
+*online* manner and manually keep track of the pairs that are created.
+:class:`ZigzagPersistence` and :class:`CohomologyPersistence` accept their
+input this way,
+
+
+Offline
+^^^^^^^
+
+For the first approach, i.e. to use :class:`StaticPersistence`, one must put the
+sort the filtration with respect to some ordering
+(for example, :func:`data_dim_cmp` for alpha shapes or :meth:`Rips.cmp` for the
+Rips complex)::
+
+ simplices.sort(data_dim_cmp) # for the alpha shapes
+ simplices.sort(rips.cmp) # for the rips complex
+
+Creating an instance of :class:`StaticPersistence` initialized with the
+filtration really initializes a boundary matrix. The subsequent call to
+:meth:`~StaticPersistence.pair_simplices` reduces the matrix to compute the
+persistence of the filtration::
+
+ p = StaticPersistence(simplices)
+ p.pair_simplices()
+
+Once the simplices are paired, one may examine the pairings by iterating over
+the instance of :class:`StaticPersistence`. We can use an auxilliary map ``smap``
+to remap the persistence indices into the actual simplices::
+
+ smap = p.make_simplex_map(simplices)
+ for i in p:
+ if i.sign():
+ birth = smap[i]
+ if i.unpaired():
+ print birth.dimension(), birth.data, "inf"
+ continue
+
+ death = smap[i.pair()]
+ print birth.dimension(), birth.data, death.data
+
+The iterator ``i`` over the instance ``p`` of :class:`StaticPersistence` is of type
+:class:`SPNode`, and represents individual simplices taken in the filtration
+order. It knows about its own :meth:`~SPNode.sign` and :meth:`~SPNode.pair` as well as
+whether it is :math:`~SPNode.unpaired`. :meth:`StaticPersistence.make_simplex_map` creates
+a map that we use to get the actual simplices: ``smap[i]`` and ``smap[i.pair()]``.
+The previous code snippet prints out the persistence diagrams of the given
+filtration.
+
+
+Online
+^^^^^^
+
+Class :class:`ZigzagPersistence` accepts additions and removals of the simplices
+one by one, and returns an internal representation of the simplices.
+(:class:`CohomologyPersistence` works in the same way, but accepts only
+additions of the simplices.)
+When one
+adds a simplex via :meth:`ZigzagPersistence.add`, one must provide its boundary
+in this internal representation together with a *birth value* which
+:class:`ZigzagPersistence` will store in case a class is born as a result of the
+addition. :meth:`~ZigzagPersistence.add` returns a pair ``(i,d)``. ``i`` is the
+internal representation of the newly added simplex, which one must record for
+future use in the boundaries of its cofaces (below it is recorded in the
+dictionary ``complex``). ``d`` is `None` in case of the birth, otherwise, it is the
+previously recorded birth value of the class that dies as a result of the
+addition. The following code adds all the ``simplices`` to a zigzag::
+
+ simplices.sort(data_dim_cmp)
+ complex = {}
+ zz = ZigzagPersistence()
+ for s in simplices:
+ i,d = zz.add([complex[sb] for sb in s.boundary], (s.dimension(), s.data))
+ complex[s] = i
+ if d is not None: # we have a death
+ dimension, birth = d # we previously stored the (dimension, data) pair
+ print dimension, birth, s.data
+
+Similarly, one can remove simplices by calling :meth:`ZigzagPersistence.remove`
+with the internal index previously returned by :meth:`~ZigzagPersistence.add`::
+
+ for s in reversed(simplices):
+ d = zz.remove(complex[s], (s.dimension() - 1, s.data))
+ del complex[s]
+ if d is not None:
+ dimension, birth = d
+ print dimension, birth, s.data
+
+Naturally, :meth:`~ZigzagPersistence.remove` returns only `d`.
+
+If one wants to add or remove an entire set of simplices "at once" (i.e. with
+all births being assigned the same value), there are two helper functions:
+:func:`add_simplices` and :func:`remove_simplices`.
+
+See the :ref:`bindings reference <python-bindings>` for more details, and
+:ref:`triangle-zigzag-example` for an example of :class:`ZigzagPersistence`.
+See :ref:`rips-pairwise-cohomology` for an example of
+:class:`CohomologyPersistence`.
+
+
+.. _speed-up-suggestions:
+
+Speed-up suggestions
+--------------------
+
+Currently, when the choice comes between efficiency and flexibility, the Python
+bindings err on the side of flexibility. There is hope that in the future the
+choice won't really be necessary. Meanwhile, one can use a few techniques that
+speed up computation at the expense of memory. Note, however, that since the
+recent switch of :class:`PairwiseDistances` to C++ rather than pure Python, it
+is not clear whether these deliver a substantial speed-up:
+
+* To avoid (possibly expensive) computation of distances during Rips complex
+ generation, store :class:`ExplicitDistances` (see :ref:`distances`)::
+
+ distances = PairwiseDistances(points)
+ distances = ExplicitDistances(distances)
+
+* To avoid the computation of simplex sizes in the Rips complex during the
+ initialization of a :class:`Filtration`, store them explicitly in
+ :attr:`Simplex.data` attribute (this is not done by default to save memory);
+ then use :func:`data_dim_cmp` when sorting the
+ :class:`Filtration`::
+
+ rips = Rips(distances)
+ simplices = Filtration()
+ rips.generate(..., simplices.append)
+ for s in simplices: s.data = rips.eval(s)
+ simplices.sort(data_dim_cmp)
+
+
+
+.. include:: substitutions.aux
--- a/examples/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,5 +1,15 @@
-add_subdirectory (alphashapes)
-add_subdirectory (ar-vineyard)
-add_subdirectory (cech-complex)
-add_subdirectory (grid)
-add_subdirectory (triangle)
+add_executable (bottleneck-distance bottleneck-distance.cpp)
+target_link_libraries (bottleneck-distance ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+
+add_subdirectory (alphashapes)
+#add_subdirectory (ar-vineyard)
+add_subdirectory (cech-complex)
+add_subdirectory (consistency)
+add_subdirectory (cohomology)
+add_subdirectory (fitness)
+add_subdirectory (pl-functions)
+add_subdirectory (triangle)
+add_subdirectory (poincare)
+add_subdirectory (rips)
+add_subdirectory (filtration)
+add_subdirectory (homology-zigzags)
--- a/examples/alphashapes/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/alphashapes/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,8 +1,27 @@
-set (targets
- alphashapes3d
- alpharadius)
-
-foreach (t ${targets})
- add_executable (${t} ${t}.cpp ${external_sources})
- target_link_libraries (${t} ${libraries} ${cgal_libraries})
-endforeach (t ${targets})
+set (libraries ${libraries}
+ ${Boost_SERIALIZATION_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY})
+
+# Build compare-diagrams
+add_executable (compare-diagrams compare-diagrams.cpp)
+target_link_libraries (compare-diagrams ${libraries})
+
+# Add targets that depend on CGAL
+if (CGAL_FOUND)
+ include (${CGAL_USE_FILE})
+
+ set (targets alphashapes3d
+ alphashapes2d
+ alphashapes3d-cohomology
+ #alpharadius
+ )
+ add_definitions (${CGAL_CXX_FLAGS_INIT})
+ include_directories (${CGAL_INCLUDE_DIRS})
+
+ foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES})
+ endforeach (t ${targets})
+else (CGAL_FOUND)
+ message(STATUS "CGAL not found, therefore alphashapes will not be built.")
+endif (CGAL_FOUND)
--- a/examples/alphashapes/alpharadius.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/alphashapes/alpharadius.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,5 +1,4 @@
-#include <utilities/sys.h>
-#include <utilities/debug.h>
+#include <utilities/log.h>
#include "alphashapes3d.h"
#include <topology/filtration.h>
@@ -48,11 +47,13 @@
int main(int argc, char** argv)
{
-#ifdef CWDEBUG
- Debug(dc::filtration.on());
- //Debug(dc::cycle.on());
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
- dionysus::debug::init();
+ stdoutLog.subscribeTo( RLOG_CHANNEL("info") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("error") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("topology/filtration") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("topology/cycle") );
#endif
std::istream& in = std::cin;
@@ -66,7 +67,7 @@
Point p(x,y,z);
Dt.insert(p);
}
- std::cout << "Delaunay triangulation computed" << std::endl;
+ rInfo("Delaunay triangulation computed");
AlphaSimplex3DVector alpha_ordering;
fill_alpha_order(Dt, alpha_ordering);
@@ -83,17 +84,17 @@
continue;
double current_alpha = CGAL::to_double(cur->value());
- std::cout << "Current alpha: " << current_alpha << std::endl;
+ rInfo("Current alpha: %f", current_alpha);
std::sort(radius_ordering.begin(), radius_ordering.end(), ro);
- std::cout << "Radius ordering size: " << radius_ordering.size() << std::endl;
+ rInfo("Radius ordering size: %i", radius_ordering.size());
RadiusFiltration rf;
for (SimplexVector::const_iterator cur = radius_ordering.begin(); cur != radius_ordering.end(); ++cur)
rf.append(*cur);
rf.fill_simplex_index_map();
- std::cout << "Simplex index map filled" << std::endl;
+ rInfo("Simplex index map filled");
rf.pair_simplices(rf.begin(), rf.end());
- std::cout << "Pairing computed" << std::endl;
+ rInfo("Pairing computed");
for (RadiusFiltration::const_Index cur = rf.begin(); cur != rf.end(); ++cur)
{
@@ -101,7 +102,7 @@
RealValue d1 = cur->distance;
//if (cur == cur->pair())
- // std::cout << "Unpaired " << cur->dimension() << ' ' << CGAL::to_double(d1) << std::endl;
+ // rInfo("Unpaired %d %f", cur->dimension(), CGAL::to_double(d1));
RealValue d2 = cur->pair()->distance;
if (d1 == d2) continue;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/alphashapes/alphashapes.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,42 @@
+# Computes the persistence diagram of the alpha shapes in both 2D and 3D
+# (decided dynamically based on the input file)
+
+from dionysus import Filtration, StaticPersistence, data_dim_cmp, vertex_cmp, \
+ fill_alpha3D_complex, fill_alpha2D_complex, points_file
+from sys import argv, exit
+from math import sqrt
+
+
+if len(argv) < 2:
+ print "Usage: %s POINTS" % argv[0]
+ exit()
+
+points = [p for p in points_file(argv[1])]
+f = Filtration()
+if len(points[0]) == 2: # 2D
+ fill_alpha2D_complex(points, f)
+elif len(points[1]) == 3: # 3D
+ fill_alpha3D_complex(points, f)
+
+print "Total number of simplices:", len(f)
+
+f.sort(data_dim_cmp)
+print "Filtration initialized"
+
+p = StaticPersistence(f)
+print "StaticPersistence initialized"
+
+p.pair_simplices()
+print "Simplices paired"
+
+print "Outputting persistence diagram"
+smap = p.make_simplex_map(f)
+for i in p:
+ if i.sign():
+ b = smap[i]
+ if i.unpaired():
+ print b.dimension(), sqrt(b.data[0]), "inf"
+ continue
+
+ d = smap[i.pair()]
+ print b.dimension(), sqrt(b.data[0]), sqrt(d.data[0])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/alphashapes/alphashapes2d.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,69 @@
+#include <utilities/log.h>
+
+#include "alphashapes2d.h"
+#include <topology/filtration.h>
+#include <topology/static-persistence.h>
+#include <topology/persistence-diagram.h>
+#include <iostream>
+
+#include <fstream>
+
+
+typedef Filtration<AlphaSimplex2D> AlphaFiltration;
+typedef StaticPersistence<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stdoutLog.subscribeTo( RLOG_CHANNEL("error") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("info") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("topology/filtration") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("topology/cycle") );
+#endif
+
+ SetFrequency(GetCounter("filtration/pair"), 10000);
+ SetTrigger(GetCounter("filtration/pair"), GetCounter(""));
+
+ // Read in the point set and compute its Delaunay triangulation
+ std::istream& in = std::cin;
+ double x,y;
+ Delaunay2D Dt;
+ while(in)
+ {
+ in >> x >> y;
+ if (!in) break;
+ Point p(x,y);
+ Dt.insert(p);
+ }
+ rInfo("Delaunay triangulation computed");
+
+ AlphaFiltration af;
+ fill_complex(Dt, af);
+ rInfo("Simplices: %i", af.size());
+
+ // Create the alpha-shape filtration
+ af.sort(AlphaSimplex2D::AlphaOrder());
+ rInfo("Filtration initialized");
+
+ Persistence p(af);
+ rInfo("Persistence initializaed");
+
+ p.pair_simplices();
+ rInfo("Simplices paired");
+
+ Persistence::SimplexMap<AlphaFiltration> m = p.make_simplex_map(af);
+ std::map<Dimension, PDgm> dgms;
+ init_diagrams(dgms, p.begin(), p.end(),
+ evaluate_through_map(m, AlphaSimplex2D::AlphaValueEvaluator()),
+ evaluate_through_map(m, AlphaSimplex2D::DimensionExtractor()));
+
+#if 1
+ std::cout << 0 << std::endl << dgms[0] << std::endl;
+ std::cout << 1 << std::endl << dgms[1] << std::endl;
+#endif
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/alphashapes/alphashapes2d.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,84 @@
+/**
+ * Author: Dmitriy Morozov
+ * Department of Computer Science, Duke University, 2007
+ */
+
+#ifndef __ALPHASHAPES2D_H__
+#define __ALPHASHAPES2D_H__
+
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/Delaunay_triangulation_2.h>
+
+#include <topology/simplex.h>
+#include <utilities/types.h>
+
+#include <vector>
+#include <set>
+#include <iostream>
+
+struct K: CGAL::Exact_predicates_exact_constructions_kernel {};
+
+typedef CGAL::Delaunay_triangulation_2<K> Delaunay2D;
+typedef Delaunay2D::Point Point;
+typedef Delaunay2D::Vertex_handle Vertex_handle;
+typedef Delaunay2D::Face_handle Face_handle;
+typedef K::FT RealValue;
+
+typedef Delaunay2D::Finite_vertices_iterator Vertex_iterator;
+typedef Delaunay2D::Finite_edges_iterator Edge_iterator;
+typedef Delaunay2D::Finite_faces_iterator Face_iterator;
+
+
+class AlphaSimplex2D: public Simplex<Vertex_handle>
+{
+ public:
+ typedef Simplex<Vertex_handle> Parent;
+ typedef std::set<AlphaSimplex2D, Parent::VertexComparison> SimplexSet;
+ typedef Parent::VertexContainer VertexSet;
+
+ public:
+ AlphaSimplex2D() {}
+ AlphaSimplex2D(const Parent& p):
+ Parent(p) {}
+ AlphaSimplex2D(const AlphaSimplex2D& s):
+ Parent(s) { attached_ = s.attached_; alpha_ = s.alpha_; }
+ AlphaSimplex2D(const Delaunay2D::Vertex& v);
+
+ AlphaSimplex2D(const Delaunay2D::Edge& e);
+ AlphaSimplex2D(const Delaunay2D::Edge& e, const SimplexSet& simplices, const Delaunay2D& Dt);
+
+ AlphaSimplex2D(const Delaunay2D::Face& c);
+
+ RealType value() const { return CGAL::to_double(alpha_); }
+ RealValue alpha() const { return alpha_; }
+ bool attached() const { return attached_; }
+
+ // Ordering
+ struct AlphaOrder
+ { bool operator()(const AlphaSimplex2D& first, const AlphaSimplex2D& second) const; };
+
+ struct AlphaValueEvaluator
+ {
+ typedef AlphaSimplex2D first_argument_type;
+ typedef RealType result_type;
+
+ RealType operator()(const AlphaSimplex2D& s) const { return s.value(); }
+ };
+
+ std::ostream& operator<<(std::ostream& out) const;
+
+ private:
+ RealValue alpha_;
+ bool attached_;
+};
+
+typedef std::vector<AlphaSimplex2D> AlphaSimplex2DVector;
+void fill_simplex_set(const Delaunay2D& Dt, AlphaSimplex2D::SimplexSet& simplices);
+template<class Filtration>
+void fill_complex(const Delaunay2D& Dt, Filtration& filtration);
+
+std::ostream& operator<<(std::ostream& out, const AlphaSimplex2D& s) { return s.operator<<(out); }
+
+#include "alphashapes2d.hpp"
+
+#endif // __ALPHASHAPES2D_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/alphashapes/alphashapes2d.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,123 @@
+#include <utilities/log.h>
+#include <boost/foreach.hpp>
+
+AlphaSimplex2D::
+AlphaSimplex2D(const Delaunay2D::Vertex& v): alpha_(0), attached_(false)
+{
+ for (int i = 0; i < 3; ++i)
+ if (v.face()->vertex(i) != Vertex_handle() && v.face()->vertex(i)->point() == v.point())
+ Parent::add(v.face()->vertex(i));
+}
+
+AlphaSimplex2D::
+AlphaSimplex2D(const Delaunay2D::Edge& e): attached_(false)
+{
+ Face_handle f = e.first;
+ for (int i = 0; i < 3; ++i)
+ if (i != e.second)
+ Parent::add(f->vertex(i));
+}
+
+AlphaSimplex2D::
+AlphaSimplex2D(const Delaunay2D::Edge& e, const SimplexSet& simplices, const Delaunay2D& Dt): attached_(false)
+{
+ Face_handle f = e.first;
+ for (int i = 0; i < 3; ++i)
+ if (i != e.second)
+ Parent::add(f->vertex(i));
+
+ VertexSet::const_iterator v = static_cast<const Parent*>(this)->vertices().begin();
+ const Point& p1 = (*v++)->point();
+ const Point& p2 = (*v)->point();
+
+ Face_handle o = f->neighbor(e.second);
+ if (o == Face_handle())
+ {
+ alpha_ = CGAL::squared_radius(p1, p2);
+ return;
+ }
+ int oi = o->index(f);
+
+ attached_ = false;
+ if (!Dt.is_infinite(f->vertex(e.second)) &&
+ CGAL::side_of_bounded_circle(p1, p2,
+ f->vertex(e.second)->point()) == CGAL::ON_BOUNDED_SIDE)
+ attached_ = true;
+ else if (!Dt.is_infinite(o->vertex(oi)) &&
+ CGAL::side_of_bounded_circle(p1, p2,
+ o->vertex(oi)->point()) == CGAL::ON_BOUNDED_SIDE)
+ attached_ = true;
+ else
+ alpha_ = CGAL::squared_radius(p1, p2);
+
+ if (attached_)
+ {
+ if (Dt.is_infinite(f))
+ alpha_ = simplices.find(AlphaSimplex2D(*o))->alpha();
+ else if (Dt.is_infinite(o))
+ alpha_ = simplices.find(AlphaSimplex2D(*f))->alpha();
+ else
+ alpha_ = std::min(simplices.find(AlphaSimplex2D(*f))->alpha(),
+ simplices.find(AlphaSimplex2D(*o))->alpha());
+ }
+}
+
+AlphaSimplex2D::
+AlphaSimplex2D(const Delaunay2D::Face& f): attached_(false)
+{
+ for (int i = 0; i < 3; ++i)
+ Parent::add(f.vertex(i));
+ VertexSet::const_iterator v = static_cast<const Parent*>(this)->vertices().begin();
+ Point p1 = (*v++)->point();
+ Point p2 = (*v++)->point();
+ Point p3 = (*v)->point();
+ alpha_ = CGAL::squared_radius(p1, p2, p3);
+}
+
+
+bool
+AlphaSimplex2D::AlphaOrder::
+operator()(const AlphaSimplex2D& first, const AlphaSimplex2D& second) const
+{
+ if (first.alpha() == second.alpha())
+ return (first.dimension() < second.dimension());
+ else
+ return (first.alpha() < second.alpha());
+}
+
+std::ostream&
+AlphaSimplex2D::
+operator<<(std::ostream& out) const
+{
+ for (VertexSet::const_iterator cur = Parent::vertices().begin();
+ cur != Parent::vertices().end(); ++cur)
+ out << **cur << ", ";
+ out << "value = " << value();
+
+ return out;
+}
+
+void fill_simplex_set(const Delaunay2D& Dt, AlphaSimplex2D::SimplexSet& simplices)
+{
+ for(Face_iterator cur = Dt.finite_faces_begin(); cur != Dt.finite_faces_end(); ++cur)
+ simplices.insert(AlphaSimplex2D(*cur));
+ rInfo("Faces inserted");
+ for(Edge_iterator cur = Dt.finite_edges_begin(); cur != Dt.finite_edges_end(); ++cur)
+ simplices.insert(AlphaSimplex2D(*cur, simplices, Dt));
+ rInfo("Edges inserted");
+ for(Vertex_iterator cur = Dt.finite_vertices_begin(); cur != Dt.finite_vertices_end(); ++cur)
+ simplices.insert(AlphaSimplex2D(*cur));
+ rInfo("Vertices inserted");
+}
+
+template<class Filtration>
+void fill_complex(const Delaunay2D& Dt, Filtration& filtration)
+{
+ // Compute all simplices with their alpha values and attachment information
+ // TODO: this can be optimized; the new Filtration can act as a SimplexSet
+ AlphaSimplex2D::SimplexSet simplices;
+ fill_simplex_set(Dt, simplices);
+ BOOST_FOREACH(const AlphaSimplex2D& s, simplices)
+ filtration.push_back(s);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/alphashapes/alphashapes3d-cohomology.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,132 @@
+#include "alphashapes3d.h"
+#include "../cohomology/wrappers.h"
+
+#include <topology/cohomology-persistence.h>
+
+#include <utilities/log.h>
+#include <utilities/timer.h>
+
+#include <iostream>
+#include <fstream>
+
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+#include <boost/foreach.hpp>
+
+
+typedef boost::tuple<Dimension, RealType> BirthInfo;
+typedef CohomologyPersistence<BirthInfo> Persistence;
+
+typedef Persistence::SimplexIndex Index;
+typedef Persistence::Death Death;
+typedef Persistence::CocyclePtr CocyclePtr;
+
+namespace po = boost::program_options;
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stdoutLog.subscribeTo( RLOG_CHANNEL("info") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("error") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("topology/persistence") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("topology/chain") );
+#endif
+
+ std::string infilename, outfilename;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose alpha shape filtration and persistence we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Where to write the collection of persistence diagrams");
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+ if (!vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " input-file output-file" << std::endl;
+ // std::cout << hidden << std::endl;
+ return 1;
+ }
+
+ std::ofstream diagram_out(outfilename.c_str());
+
+ Timer total_timer; total_timer.start();
+
+ // Read in the point set and compute its Delaunay triangulation
+ std::ifstream in(infilename.c_str());
+ double x,y,z;
+ Delaunay3D Dt;
+ while(in)
+ {
+ in >> x >> y >> z;
+ Point p(x,y,z);
+ Dt.insert(p);
+ }
+ rInfo("Delaunay triangulation computed");
+
+ // Set up the alpha shape filtration
+ typedef std::vector<AlphaSimplex3D> AlphaSimplex3DVector;
+ AlphaSimplex3DVector complex;
+ fill_complex(Dt, complex);
+ rInfo("Simplices: %d", complex.size());
+ std::sort(complex.begin(), complex.end(), AlphaSimplex3D::AlphaOrder());
+
+ Timer persistence_timer; persistence_timer.start();
+ std::map<AlphaSimplex3D, Index, AlphaSimplex3D::VertexComparison> complex_map;
+ Persistence p;
+ boost::progress_display show_progress(complex.size());
+
+ #ifdef COUNTERS
+ Counter::CounterType max_element_count = 0;
+ #endif
+
+ for(AlphaSimplex3DVector::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ {
+ const AlphaSimplex3D& s = *cur;
+ std::vector<Index> boundary;
+ for (AlphaSimplex3D::BoundaryIterator bcur = s.boundary_begin(); bcur != s.boundary_end(); ++bcur)
+ boundary.push_back(complex_map[*bcur]);
+
+ Index idx; Death d; CocyclePtr ccl;
+ bool store = s.dimension() < 3;
+ boost::tie(idx, d, ccl) = p.add(boundary.begin(), boundary.end(), boost::make_tuple(s.dimension(), s.value()), store);
+
+ // c[*cur] = idx;
+ if (store)
+ complex_map[s] = idx;
+
+ if (d && (s.value() - d->get<1>()) > 0)
+ {
+ AssertMsg(d->get<0>() == s.dimension() - 1, "Dimensions must match");
+ diagram_out << (s.dimension() - 1) << " " << d->get<1>() << " " << s.value() << std::endl;
+ }
+ ++show_progress;
+
+ #ifdef COUNTERS
+ max_element_count = std::max(max_element_count, cCohomologyElementCount->count);
+ #endif
+ }
+ // output infinte persistence pairs
+ for (Persistence::CocycleIndex cur = p.begin(); cur != p.end(); ++cur)
+ diagram_out << cur->birth.get<0>() << " " << cur->birth.get<1>() << " inf" << std::endl;
+ persistence_timer.stop();
+
+ total_timer.stop();
+ persistence_timer.check("Persistence timer");
+ total_timer.check("Total timer");
+
+ #ifdef COUNTERS
+ std::cout << "Max element count: " << max_element_count << std::endl;
+ #endif
+}
--- a/examples/alphashapes/alphashapes3d.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/alphashapes/alphashapes3d.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,38 +1,106 @@
-#include <utilities/sys.h>
-#include <utilities/debug.h>
+#include <utilities/log.h>
+#include <utilities/timer.h>
#include "alphashapes3d.h"
#include <topology/filtration.h>
+#include <topology/static-persistence.h>
+#include <topology/persistence-diagram.h>
#include <iostream>
+
#include <fstream>
+#include <boost/archive/binary_oarchive.hpp>
+#include <boost/serialization/map.hpp>
+
+#include <boost/program_options.hpp>
-typedef Filtration<AlphaSimplex3D> AlphaFiltration;
+typedef Filtration<AlphaSimplex3D> AlphaFiltration;
+typedef StaticPersistence<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+
+namespace po = boost::program_options;
int main(int argc, char** argv)
{
- // Read in the point set and compute its Delaunay triangulation
- std::istream& in = std::cin;
- double x,y,z;
- Delaunay Dt;
- while(in)
- {
- in >> x >> y >> z;
- Point p(x,y,z);
- Dt.insert(p);
- }
- std::cout << "Delaunay triangulation computed" << std::endl;
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stdoutLog.subscribeTo( RLOG_CHANNEL("info") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("error") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("topology/persistence") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("topology/chain") );
+#endif
+
+ // SetFrequency(GetCounter("persistence/pair"), 10000);
+ // SetTrigger(GetCounter("persistence/pair"), GetCounter(""));
+
+ std::string infilename, outfilename;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose alpha shape filtration and persistence we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Where to write the collection of persistence diagrams");
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+ if (!vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " input-file output-file" << std::endl;
+ std::cout << hidden << std::endl;
+ return 1;
+ }
+
+
+ // Read in the point set and compute its Delaunay triangulation
+ std::ifstream in(infilename.c_str());
+ double x,y,z;
+ Delaunay3D Dt;
+ while(in)
+ {
+ in >> x >> y >> z;
+ Point p(x,y,z);
+ Dt.insert(p);
+ }
+ rInfo("Delaunay triangulation computed");
- AlphaSimplex3DVector alpha_ordering;
- fill_alpha_order(Dt, alpha_ordering);
- std::cout << "Simplices: " << alpha_ordering.size() << std::endl;
+ AlphaFiltration af;
+ fill_complex(Dt, af);
+ rInfo("Simplices: %d", af.size());
+
+ // Create the alpha-shape filtration
+ af.sort(AlphaSimplex3D::AlphaOrder());
+ rInfo("Filtration initialized");
+
+ Persistence p(af);
+ rInfo("Persistence initializaed");
- // Create the alpha-shape filtration
- AlphaFiltration af;
- for (AlphaSimplex3DVector::const_iterator cur = alpha_ordering.begin(); cur != alpha_ordering.end(); ++cur)
- af.append(*cur);
- af.fill_simplex_index_map();
- af.pair_simplices(af.begin(), af.end());
- std::cout << "Simplices paired" << std::endl;
+ Timer persistence_timer; persistence_timer.start();
+ p.pair_simplices();
+ persistence_timer.stop();
+ rInfo("Simplices paired");
+ persistence_timer.check("Persistence timer");
+
+ Persistence::SimplexMap<AlphaFiltration> m = p.make_simplex_map(af);
+ std::map<Dimension, PDgm> dgms;
+ init_diagrams(dgms, p.begin(), p.end(),
+ evaluate_through_map(m, AlphaSimplex3D::AlphaValueEvaluator()),
+ evaluate_through_map(m, AlphaSimplex3D::DimensionExtractor()));
+#if 0
+ std::cout << 0 << std::endl << dgms[0] << std::endl;
+ std::cout << 1 << std::endl << dgms[1] << std::endl;
+ std::cout << 2 << std::endl << dgms[2] << std::endl;
+#endif
+
+ std::ofstream ofs(outfilename.c_str());
+ boost::archive::binary_oarchive oa(ofs);
+ oa << dgms;
}
-
--- a/examples/alphashapes/alphashapes3d.h Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/alphashapes/alphashapes3d.h Tue Jun 27 09:37:05 2017 -0700
@@ -18,30 +18,25 @@
struct K: CGAL::Exact_predicates_exact_constructions_kernel {};
-typedef CGAL::Delaunay_triangulation_3<K> Delaunay;
-typedef Delaunay::Point Point;
-typedef Delaunay::Vertex Vertex;
-typedef Delaunay::Vertex_handle Vertex_handle;
-typedef Delaunay::Edge Edge;
-typedef Delaunay::Facet Facet;
-typedef Delaunay::Cell Cell;
-typedef Delaunay::Cell_handle Cell_handle;
-typedef K::FT RealValue;
+typedef CGAL::Delaunay_triangulation_3<K> Delaunay3D;
+typedef Delaunay3D::Point Point;
+typedef Delaunay3D::Vertex_handle Vertex_handle;
+typedef Delaunay3D::Cell_handle Cell_handle;
+typedef K::FT RealValue;
-typedef Delaunay::Finite_vertices_iterator Vertex_iterator;
-typedef Delaunay::Finite_edges_iterator Edge_iterator;
-typedef Delaunay::Finite_facets_iterator Facet_iterator;
-typedef Delaunay::Finite_cells_iterator Cell_iterator;
-typedef Delaunay::Facet_circulator Facet_circulator;
+typedef Delaunay3D::Finite_vertices_iterator Vertex_iterator;
+typedef Delaunay3D::Finite_edges_iterator Edge_iterator;
+typedef Delaunay3D::Finite_facets_iterator Facet_iterator;
+typedef Delaunay3D::Finite_cells_iterator Cell_iterator;
+typedef Delaunay3D::Facet_circulator Facet_circulator;
-class AlphaSimplex3D: public SimplexWithVertices<Vertex_handle>
+class AlphaSimplex3D: public Simplex<Vertex_handle>
{
public:
- typedef std::set<AlphaSimplex3D> SimplexSet;
- typedef SimplexWithVertices<Vertex_handle> Parent;
+ typedef Simplex<Vertex_handle> Parent;
+ typedef std::set<AlphaSimplex3D, Parent::VertexComparison> SimplexSet;
typedef Parent::VertexContainer VertexSet;
- typedef std::list<AlphaSimplex3D> Cycle;
public:
AlphaSimplex3D() {}
@@ -49,25 +44,32 @@
Parent(p) {}
AlphaSimplex3D(const AlphaSimplex3D& s):
Parent(s) { attached_ = s.attached_; alpha_ = s.alpha_; }
- AlphaSimplex3D(const ::Vertex& v);
+ AlphaSimplex3D(const Delaunay3D::Vertex& v);
- AlphaSimplex3D(const Edge& e);
- AlphaSimplex3D(const Edge& e, const SimplexSet& simplices, Facet_circulator facet_bg);
+ AlphaSimplex3D(const Delaunay3D::Edge& e);
+ AlphaSimplex3D(const Delaunay3D::Edge& e, const SimplexSet& simplices, const Delaunay3D& Dt, Facet_circulator facet_bg);
- AlphaSimplex3D(const Facet& f);
- AlphaSimplex3D(const Facet& f, const SimplexSet& simplices);
+ AlphaSimplex3D(const Delaunay3D::Facet& f);
+ AlphaSimplex3D(const Delaunay3D::Facet& f, const SimplexSet& simplices, const Delaunay3D& Dt);
- AlphaSimplex3D(const Cell& c);
+ AlphaSimplex3D(const Delaunay3D::Cell& c);
RealType value() const { return CGAL::to_double(alpha_); }
RealValue alpha() const { return alpha_; }
bool attached() const { return attached_; }
- Cycle boundary() const;
// Ordering
struct AlphaOrder
{ bool operator()(const AlphaSimplex3D& first, const AlphaSimplex3D& second) const; };
-
+
+ struct AlphaValueEvaluator
+ {
+ typedef AlphaSimplex3D first_argument_type;
+ typedef RealType result_type;
+
+ RealType operator()(const AlphaSimplex3D& s) const { return s.value(); }
+ };
+
std::ostream& operator<<(std::ostream& out) const;
private:
@@ -75,9 +77,9 @@
bool attached_;
};
-typedef std::vector<AlphaSimplex3D> AlphaSimplex3DVector;
-void fill_alpha_order(const Delaunay& Dt,
- AlphaSimplex3DVector& alpha_order);
+void fill_simplex_set(const Delaunay3D& Dt, AlphaSimplex3D::SimplexSet& simplices);
+template<class Filtration>
+void fill_complex(const Delaunay3D& Dt, Filtration& filtration);
std::ostream& operator<<(std::ostream& out, const AlphaSimplex3D& s) { return s.operator<<(out); }
--- a/examples/alphashapes/alphashapes3d.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/alphashapes/alphashapes3d.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,182 +1,171 @@
-AlphaSimplex3D::
-AlphaSimplex3D(const ::Vertex& v): alpha_(0), attached_(false)
+#include <utilities/log.h>
+#include <boost/foreach.hpp>
+
+AlphaSimplex3D::
+AlphaSimplex3D(const Delaunay3D::Vertex& v): alpha_(0), attached_(false)
{
- for (int i = 0; i < 4; ++i)
- if (v.cell()->vertex(i)->point() == v.point())
- Parent::add(v.cell()->vertex(i));
+ for (int i = 0; i < 4; ++i)
+ if (v.cell()->vertex(i)->point() == v.point())
+ Parent::add(v.cell()->vertex(i));
}
-AlphaSimplex3D::
-AlphaSimplex3D(const Edge& e)
+AlphaSimplex3D::
+AlphaSimplex3D(const Delaunay3D::Edge& e)
{
Cell_handle c = e.first;
- Parent::add(c->vertex(e.second));
- Parent::add(c->vertex(e.third));
+ Parent::add(c->vertex(e.second));
+ Parent::add(c->vertex(e.third));
}
-AlphaSimplex3D::
-AlphaSimplex3D(const Edge& e, const SimplexSet& simplices, Facet_circulator facet_bg)
+AlphaSimplex3D::
+AlphaSimplex3D(const Delaunay3D::Edge& e, const SimplexSet& simplices, const Delaunay3D& Dt, Facet_circulator facet_bg)
{
Cell_handle c = e.first;
- Parent::add(c->vertex(e.second));
- Parent::add(c->vertex(e.third));
+ Parent::add(c->vertex(e.second));
+ Parent::add(c->vertex(e.third));
- Facet_circulator cur = facet_bg;
- SimplexSet::const_iterator cur_iter = simplices.find(AlphaSimplex3D(*cur));
- while (cur_iter == simplices.end())
- {
- ++cur;
- cur_iter = simplices.find(AlphaSimplex3D(*cur));
- }
- RealValue min = cur_iter->alpha();
-
- VertexSet::const_iterator v = Parent::vertices().begin();
- const Point& p1 = (*v++)->point();
- const Point& p2 = (*v)->point();
- attached_ = false;
+ Facet_circulator cur = facet_bg;
+ while (Dt.is_infinite(*cur)) ++cur;
+ SimplexSet::const_iterator cur_iter = simplices.find(AlphaSimplex3D(*cur));
+ RealValue min = cur_iter->alpha();
+
+ const VertexSet& vertices = static_cast<const Parent*>(this)->vertices();
+ VertexSet::const_iterator v = vertices.begin();
+ const Point& p1 = (*v++)->point();
+ const Point& p2 = (*v)->point();
+ attached_ = false;
- if (facet_bg != 0) do
- {
- VertexSet::const_iterator v = Parent::vertices().begin();
- int i0 = (*cur).first->index(*v++);
- int i1 = (*cur).first->index(*v);
- int i = 6 - i0 - i1 - (*cur).second;
- Point p3 = (*cur).first->vertex(i)->point();
+ if (facet_bg != 0) do
+ {
+ VertexSet::const_iterator v = vertices.begin();
+ int i0 = (*cur).first->index(*v++);
+ int i1 = (*cur).first->index(*v);
+ int i = 6 - i0 - i1 - (*cur).second;
+ if (Dt.is_infinite(cur->first->vertex(i))) { ++cur; continue; }
+ Point p3 = (*cur).first->vertex(i)->point();
- cur_iter = simplices.find(AlphaSimplex3D(*cur));
- if (cur_iter == simplices.end()) // cur is infinite
- {
- ++cur; continue;
- }
-
- if (CGAL::side_of_bounded_sphere(p1, p2, p3) == CGAL::ON_BOUNDED_SIDE)
- attached_ = true;
- RealValue val = cur_iter->alpha();
- if (val < min)
- min = val;
- ++cur;
- } while (cur != facet_bg);
+ cur_iter = simplices.find(AlphaSimplex3D(*cur));
+ if (CGAL::side_of_bounded_sphere(p1, p2, p3) == CGAL::ON_BOUNDED_SIDE)
+ attached_ = true;
+ RealValue val = cur_iter->alpha();
+ if (val < min)
+ min = val;
+ ++cur;
+ } while (cur != facet_bg);
- if (attached_)
- alpha_ = min;
- else
- alpha_ = CGAL::squared_radius(p1, p2);
+ if (attached_)
+ alpha_ = min;
+ else
+ alpha_ = CGAL::squared_radius(p1, p2);
}
-AlphaSimplex3D::
-AlphaSimplex3D(const Facet& f)
+AlphaSimplex3D::
+AlphaSimplex3D(const Delaunay3D::Facet& f)
{
Cell_handle c = f.first;
- for (int i = 0; i < 4; ++i)
- if (i != f.second)
- Parent::add(c->vertex(i));
+ for (int i = 0; i < 4; ++i)
+ if (i != f.second)
+ Parent::add(c->vertex(i));
}
-AlphaSimplex3D::
-AlphaSimplex3D(const Facet& f, const SimplexSet& simplices)
+AlphaSimplex3D::
+AlphaSimplex3D(const Delaunay3D::Facet& f, const SimplexSet& simplices, const Delaunay3D& Dt)
{
Cell_handle c = f.first;
- for (int i = 0; i < 4; ++i)
- if (i != f.second)
- Parent::add(c->vertex(i));
+ for (int i = 0; i < 4; ++i)
+ if (i != f.second)
+ Parent::add(c->vertex(i));
- Cell_handle o = c->neighbor(f.second);
- int oi = o->index(c);
+ Cell_handle o = c->neighbor(f.second);
+ int oi = o->index(c);
+
+ VertexSet::const_iterator v = static_cast<const Parent*>(this)->vertices().begin();
+ const Point& p1 = (*v++)->point();
+ const Point& p2 = (*v++)->point();
+ const Point& p3 = (*v)->point();
- VertexSet::const_iterator v = Parent::vertices().begin();
- const Point& p1 = (*v++)->point();
- const Point& p2 = (*v++)->point();
- const Point& p3 = (*v)->point();
-
- attached_ = false;
- if (CGAL::side_of_bounded_sphere(p1, p2, p3,
- c->vertex(f.second)->point()) == CGAL::ON_BOUNDED_SIDE)
- attached_ = true;
- else if (CGAL::side_of_bounded_sphere(p1, p2, p3,
- o->vertex(oi)->point()) == CGAL::ON_BOUNDED_SIDE)
- attached_ = true;
- else
- alpha_ = squared_radius(p1, p2, p3);
-
- if (attached_)
- {
- SimplexSet::const_iterator c_iter = simplices.find(AlphaSimplex3D(*c));
- SimplexSet::const_iterator o_iter = simplices.find(AlphaSimplex3D(*o));
- if (c_iter == simplices.end()) // c is infinite
- alpha_ = o_iter->alpha();
- else if (o_iter == simplices.end()) // o is infinite
- alpha_ = c_iter->alpha();
- else
- alpha_ = std::min(c_iter->alpha(), o_iter->alpha());
- }
+ attached_ = false;
+ if (!Dt.is_infinite(c->vertex(f.second)) &&
+ CGAL::side_of_bounded_sphere(p1, p2, p3,
+ c->vertex(f.second)->point()) == CGAL::ON_BOUNDED_SIDE)
+ attached_ = true;
+ else if (!Dt.is_infinite(o->vertex(oi)) &&
+ CGAL::side_of_bounded_sphere(p1, p2, p3,
+ o->vertex(oi)->point()) == CGAL::ON_BOUNDED_SIDE)
+ attached_ = true;
+ else
+ alpha_ = CGAL::squared_radius(p1, p2, p3);
+
+ if (attached_)
+ {
+ if (Dt.is_infinite(c))
+ alpha_ = simplices.find(AlphaSimplex3D(*o))->alpha();
+ else if (Dt.is_infinite(o))
+ alpha_ = simplices.find(AlphaSimplex3D(*c))->alpha();
+ else
+ alpha_ = std::min(simplices.find(AlphaSimplex3D(*c))->alpha(),
+ simplices.find(AlphaSimplex3D(*o))->alpha());
+ }
}
-AlphaSimplex3D::
-AlphaSimplex3D(const Cell& c): attached_(false)
+AlphaSimplex3D::
+AlphaSimplex3D(const Delaunay3D::Cell& c): attached_(false)
{
- for (int i = 0; i < 4; ++i)
- Parent::add(c.vertex(i));
- VertexSet::const_iterator v = Parent::vertices().begin();
- Point p1 = (*v++)->point();
- Point p2 = (*v++)->point();
- Point p3 = (*v++)->point();
- Point p4 = (*v)->point();
- alpha_ = CGAL::squared_radius(p1, p2, p3, p4);
-}
-
-AlphaSimplex3D::Cycle
-AlphaSimplex3D::boundary() const
-{
- Cycle bdry;
- Parent::Cycle pbdry = Parent::boundary();
- for (Parent::Cycle::const_iterator cur = pbdry.begin(); cur != pbdry.end(); ++cur)
- bdry.push_back(*cur);
- return bdry;
+ for (int i = 0; i < 4; ++i)
+ Parent::add(c.vertex(i));
+ VertexSet::const_iterator v = static_cast<const Parent*>(this)->vertices().begin();
+ Point p1 = (*v++)->point();
+ Point p2 = (*v++)->point();
+ Point p3 = (*v++)->point();
+ Point p4 = (*v)->point();
+ alpha_ = CGAL::squared_radius(p1, p2, p3, p4);
}
-bool
+bool
AlphaSimplex3D::AlphaOrder::
operator()(const AlphaSimplex3D& first, const AlphaSimplex3D& second) const
{
- if (first.alpha() == second.alpha())
- return (first.dimension() < second.dimension());
- else
- return (first.alpha() < second.alpha());
+ if (first.alpha() == second.alpha())
+ return (first.dimension() < second.dimension());
+ else
+ return (first.alpha() < second.alpha());
}
-std::ostream&
+std::ostream&
AlphaSimplex3D::
operator<<(std::ostream& out) const
{
- for (VertexSet::const_iterator cur = Parent::vertices().begin(); cur != Parent::vertices().end(); ++cur)
- out << **cur << ", ";
- out << "value = " << value();
+ for (VertexSet::const_iterator cur = Parent::vertices().begin(); cur != Parent::vertices().end(); ++cur)
+ out << **cur << ", ";
+ out << "value = " << value();
- return out;
+ return out;
}
-
-void fill_alpha_order(const Delaunay& Dt, AlphaSimplex3DVector& alpha_order)
+void fill_simplex_set(const Delaunay3D& Dt, AlphaSimplex3D::SimplexSet& simplices)
{
- // Compute all simplices with their alpha values and attachment information
- AlphaSimplex3D::SimplexSet simplices;
- for(Cell_iterator cur = Dt.finite_cells_begin(); cur != Dt.finite_cells_end(); ++cur)
- simplices.insert(AlphaSimplex3D(*cur));
- std::cout << "Cells inserted" << std::endl;
- for(Facet_iterator cur = Dt.finite_facets_begin(); cur != Dt.finite_facets_end(); ++cur)
- simplices.insert(AlphaSimplex3D(*cur, simplices));
- std::cout << "Facets inserted" << std::endl;
- for(Edge_iterator cur = Dt.finite_edges_begin(); cur != Dt.finite_edges_end(); ++cur)
- simplices.insert(AlphaSimplex3D(*cur, simplices, Dt.incident_facets(*cur)));
- std::cout << "Edges inserted" << std::endl;
- for(Vertex_iterator cur = Dt.finite_vertices_begin(); cur != Dt.finite_vertices_end(); ++cur)
- simplices.insert(AlphaSimplex3D(*cur));
- std::cout << "Vertices inserted" << std::endl;
-
- // Sort simplices by their alpha values
- alpha_order.resize(simplices.size());
- std::copy(simplices.begin(), simplices.end(), alpha_order.begin());
- std::sort(alpha_order.begin(), alpha_order.end(), AlphaSimplex3D::AlphaOrder());
+ // Compute all simplices with their alpha values and attachment information
+ for(Cell_iterator cur = Dt.finite_cells_begin(); cur != Dt.finite_cells_end(); ++cur)
+ simplices.insert(AlphaSimplex3D(*cur));
+ rInfo("Cells inserted");
+ for(Facet_iterator cur = Dt.finite_facets_begin(); cur != Dt.finite_facets_end(); ++cur)
+ simplices.insert(AlphaSimplex3D(*cur, simplices, Dt));
+ rInfo("Facets inserted");
+ for(Edge_iterator cur = Dt.finite_edges_begin(); cur != Dt.finite_edges_end(); ++cur)
+ simplices.insert(AlphaSimplex3D(*cur, simplices, Dt, Dt.incident_facets(*cur)));
+ rInfo("Edges inserted");
+ for(Vertex_iterator cur = Dt.finite_vertices_begin(); cur != Dt.finite_vertices_end(); ++cur)
+ simplices.insert(AlphaSimplex3D(*cur));
+ rInfo("Vertices inserted");
}
+template<class Filtration>
+void fill_complex(const Delaunay3D& Dt, Filtration& filtration)
+{
+ AlphaSimplex3D::SimplexSet simplices;
+ fill_simplex_set(Dt, simplices);
+ BOOST_FOREACH(const AlphaSimplex3D& s, simplices)
+ filtration.push_back(s);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/alphashapes/compare-diagrams.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,56 @@
+#include <utilities/types.h>
+#include <topology/persistence-diagram.h>
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/serialization/map.hpp>
+
+#include <boost/program_options.hpp>
+
+
+typedef PersistenceDiagram<> PDgm;
+
+namespace po = boost::program_options;
+
+
+int main(int argc, char* argv[])
+{
+ std::string filename1, filename2;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file1", po::value<std::string>(&filename1), "The first collection of persistence diagrams")
+ ("input-file2", po::value<std::string>(&filename2), "The second collection of persistence diagrams");
+
+ po::positional_options_description p;
+ p.add("input-file1", 1);
+ p.add("input-file2", 2);
+
+ po::options_description all; all.add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+ if (!vm.count("input-file1") || !vm.count("input-file2"))
+ {
+ std::cout << "Usage: " << argv[0] << " input-file1 input-file2" << std::endl;
+ std::cout << hidden << std::endl;
+ return 1;
+ }
+
+ std::ifstream ifs1(filename1.c_str()), ifs2(filename2.c_str());
+ boost::archive::binary_iarchive ia1(ifs1), ia2(ifs2);
+
+ std::map<Dimension, PDgm> dgms1, dgms2;
+
+ ia1 >> dgms1;
+ ia2 >> dgms2;
+
+ std::cout << "Distance between dimension 0: " << bottleneck_distance(dgms1[0], dgms2[0]) << std::endl;
+ std::cout << "Distance between dimension 1: " << bottleneck_distance(dgms1[1], dgms2[1]) << std::endl;
+ std::cout << "Distance between dimension 2: " << bottleneck_distance(dgms1[2], dgms2[2]) << std::endl;
+}
--- a/examples/ar-vineyard/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/ar-vineyard/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,7 +1,10 @@
set (targets
ar-vineyard)
-
+
+add_definitions (${cgal_cxxflags})
foreach (t ${targets})
add_executable (${t} ${t}.cpp ${external_sources})
- target_link_libraries (${t} ${libraries} ${cgal_libraries} ${synaps_libraries})
+ target_link_libraries (${t} ${libraries} ${cgal_library} ${CGAL_3RD_PARTY_LIBRARIES}
+ ${Boost_SIGNALS_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY})
endforeach (t)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/ar-vineyard/ar-function-kernel.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,80 @@
+#ifndef __AR_FUNCTION_KERNEL_H__
+#define __AR_FUNCTION_KERNEL_H__
+
+
+#include <stack>
+#include <iostream>
+#include "ar-simplex3d.h"
+
+
+
+/**
+ * Represents function suitable for the FunctionKernel. Albeit very restrictive
+ * (it only supports subtraction), although that should suffice for KineticSort.
+ */
+class ARFunction
+{
+ public:
+ /// Represents the three forms of the involved functions. See Appendix B of LHI paper.
+ enum FunctionForm { none, rho, lambda, phi };
+
+ public:
+ ARFunction(FunctionForm f, ARSimplex3D* s):
+ form_(f), form2_(none),
+ simplex_(s), simplex2_(0) {}
+ ARFunction(const ARFunction& other):
+ form_(other.form_), form2_(other.form2_),
+ simplex_(other.simplex_),
+ simplex2_(other.simplex2_) {}
+
+ ARFunction& operator-=(const ARFunction& other) { form2_ = other.form_; simplex2_ = other.simplex_; return *this; }
+ ARFunction operator-(const ARFunction& other) { return (ARFunction(*this) -= other); }
+
+ FunctionForm form() const { return form_; }
+ FunctionForm form2() const { return form2_; }
+ ARSimplex3D* simplex() const { return simplex_; }
+ ARSimplex3D* simplex2() const { return simplex2_; }
+
+ std::ostream& operator<<(std::ostream& out) const { return (out << "(" << form_ << ", " << simplex_ << "), ("
+ << form2_ << ", " << simplex2_ << ")"); }
+
+ private:
+ FunctionForm form_, form2_; // the function is form_ - form2_
+ ARSimplex3D *simplex_, *simplex2_;
+};
+
+std::ostream&
+operator<<(std::ostream& out, const ARFunction& f)
+{ return f.operator<<(out); }
+
+/**
+ * Function kernel specialized at solving the kinds of functions involved in
+ * ARVineyard construction. We cannot use a polynomial kernel (which includes
+ * rational functions) that we already have because not all involved functions
+ * are rational.
+ */
+class ARFunctionKernel
+{
+ public:
+ typedef double FieldType;
+ typedef FieldType RootType;
+ typedef std::stack<RootType> RootStack;
+ typedef ARSimplex3D::RealValue SimplexFieldType;
+
+ typedef ARFunction Function;
+ typedef Function::FunctionForm FunctionForm;
+
+ public:
+ static void solve(const Function& f, RootStack& stack);
+ static RootType root(RootType r) { return r; }
+ static RootType root(SimplexFieldType r) { return CGAL::to_double(r); }
+ static int sign_at(const Function& f, RootType r);
+ static RootType between(RootType r1, RootType r2) { return (r1 + r2)/2; }
+ static int sign_at_negative_infinity(const Function& f);
+
+ static FieldType value_at(const Function& f, RootType v);
+};
+
+#include "ar-function-kernel.hpp"
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/ar-vineyard/ar-function-kernel.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,144 @@
+#include <utilities/log.h>
+#include <cmath>
+
+#if LOGGING
+static rlog::RLogChannel* rlARFunctionKernel = DEF_CHANNEL("ar/function-kernel", rlog::Log_Debug);
+static rlog::RLogChannel* rlARFunctionKernelValue = DEF_CHANNEL("ar/function-kernel/value", rlog::Log_Debug);
+#endif
+
+
+/* For now only rho and phi are implemented */
+void
+ARFunctionKernel::
+solve(const Function& f, RootStack& stack)
+{
+ AssertMsg(stack.empty(), "Stack must be empty before solving");
+ AssertMsg((f.form() != Function::none) && (f.form2() != Function::none), "Solving possible only for differences");
+
+ FunctionForm f1 = f.form(), f2 = f.form2();
+ const ARSimplex3D *s1 = f.simplex(),
+ *s2 = f.simplex2();
+ if (f1 < f2) { std::swap(f1,f2); std::swap(s1,s2); } // for simplicity enforce function order
+ // to handle fewer cases explicitly
+ AssertMsg(f1 != Function::lambda && f2 != Function::lambda, "Lambda not implemented yet");
+
+ rLog(rlARFunctionKernel, "Solve: function1 = (%i, %p), function2 = (%i, %p)",
+ f1, s1, f2, s2);
+
+ //if (f1 == Function::phi && f2 == Function::phi) return;
+ //if (f1 == Function::rho && f2 == Function::rho) return;
+
+ if (f1 == Function::phi && f2 == Function::rho)
+ {
+ SimplexFieldType r = s2->alpha() - s1->phi_const();
+ rLog(rlARFunctionKernel, " phi = rho => r^2 = %s (%lf)", tostring(r).c_str(), root(r));
+ stack.push(root(r));
+ }
+
+ if (f1 == Function::phi && f2 == Function::lambda)
+ {
+ rLog(rlARFunctionKernel, " phi = lambda");
+ SimplexFieldType r2 = (s2->rho() + s2->v() - s2->s() - s1->phi_const());
+ r2 *= r2;
+ r2 /= 4*s2->v();
+ r2 += s2->s();
+ if (r2 >= s2->s() + s2->v())
+ stack.push(root(r2));
+
+ SimplexFieldType r1 = s2->rho() - s1->phi_const();
+ if (r1 <= s2->s() + s2->v())
+ stack.push(root(r1));
+ }
+
+ // FIXME: this is far from complete!
+ if (f1 == Function::lambda && f2 == Function::lambda)
+ {
+ rLog(rlARFunctionKernel, " lambda = lambda");
+ if ((s1->s() + s1->v() < s2->s() + s2->v())) // let f1 be the function with larger break point
+ { std::swap(f1,f2); std::swap(s1,s2); }
+
+ if (s1->rho() > s2->rho())
+ {
+ RootType r = root(s2->s() + s2->v() + s1->rho() - s2->rho()) + 2*sqrt(root(s2->v()*(s1->rho() - s2->rho())));
+ if (r < root(s1->s() + s1->v()) && r > root(s2->s() + s2->v()))
+ stack.push(r);
+ }
+ }
+
+ if (f1 == Function::lambda && f2 == Function::rho)
+ {
+ rLog(rlARFunctionKernel, " lambda = rho");
+ // perhaps no solutions instead of an assertion is the right way to deal with this
+ AssertMsg(s2->alpha() > s1->rho(), "Rho_0^2 must be greater than Rho^2");
+
+ RootType r = sqrt(root(s2->v()*(s2->alpha() - s1->rho()))); // damn square roots
+ r *= 2;
+ r += root(s1->s() + s1->v() + s2->alpha() - s1->rho());
+ }
+ rLog(rlARFunctionKernel, " Stack size: %i", stack.size());
+ if (stack.size() > 0) rLog(rlARFunctionKernel, " Top: %lf", stack.top());
+}
+
+int
+ARFunctionKernel::
+sign_at(const Function& f, RootType r)
+{
+ FieldType v = value_at(f,r);
+ if (v > 0) return true;
+ else return false;
+}
+
+int
+ARFunctionKernel::
+sign_at_negative_infinity(const Function& f)
+{
+ FunctionForm f1 = f.form(), f2 = f.form2();
+ const ARSimplex3D *s1 = f.simplex(),
+ *s2 = f.simplex2();
+ int multiplier = 1;
+ if (f1 < f2) { std::swap(f1, f2); std::swap(s1, s2); multiplier = -1; }
+
+ AssertMsg(f1 != Function::lambda && f2 != Function::lambda, "Lambda not implemented yet");
+
+ if (f1 == Function::phi && f2 == Function::phi)
+ {
+ if (s1->phi_const() == s2->phi_const()) return 0;
+ if (s1->phi_const() > s2->phi_const()) return 1; // multiplier must be 1
+ else return -1;
+ }
+
+ if (f1 == Function::phi && f2 == Function::rho)
+ return -multiplier;
+
+ if (f1 == Function::rho && f2 == Function::rho)
+ {
+ if (s1->alpha() == s2->alpha()) return 0;
+ if (s1->alpha() > s2->alpha()) return 1; // multiplier must be 1
+ else return -1;
+ }
+
+ AssertMsg(false, "The case analysis should be exhaustive in sign at -infinity");
+ return false;
+}
+
+ARFunctionKernel::FieldType
+ARFunctionKernel::
+value_at(const Function& f, RootType v)
+{
+ FunctionForm f1 = f.form(), f2 = f.form2();
+ ARSimplex3D *s1 = f.simplex(),
+ *s2 = f.simplex2();
+
+ AssertMsg(f2 == Function::none && s2 == 0, "Value_at knows only about functions themselves, not their differences");
+ AssertMsg(f1 != Function::lambda, "Lambda not implemented yet");
+ rLog(rlARFunctionKernelValue, "Value_at: function = (%i, %p)", f1, s1);
+
+ if (f1 == Function::phi)
+ return v + root(s1->phi_const());
+
+ if (f1 == Function::rho)
+ return root(s1->alpha());
+
+ AssertMsg(false, "The case analysis should be exhaustive in value_at");
+ return 0;
+}
--- a/examples/ar-vineyard/ar-simplex3d.h Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/ar-vineyard/ar-simplex3d.h Tue Jun 27 09:37:05 2017 -0700
@@ -28,7 +28,6 @@
typedef Delaunay::Facet Facet;
typedef Delaunay::Cell Cell;
typedef Delaunay::Cell_handle Cell_handle;
-typedef K::FT RealValue;
typedef Delaunay::Finite_vertices_iterator Vertex_iterator;
typedef Delaunay::Finite_edges_iterator Edge_iterator;
@@ -40,6 +39,7 @@
class ARSimplex3D: public SimplexWithVertices<Vertex_handle>
{
public:
+ typedef K::FT RealValue;
typedef std::map<ARSimplex3D, RealValue> SimplexPhiMap;
typedef SimplexWithVertices<Vertex_handle> Parent;
typedef Parent::VertexContainer VertexSet;
@@ -56,10 +56,11 @@
ARSimplex3D(const Edge& e);
ARSimplex3D(const Edge& e, const Point& z, SimplexPhiMap& simplices,
- Facet_circulator facet_bg);
+ const Delaunay& Dt, Facet_circulator facet_bg);
ARSimplex3D(const Facet& f);
- ARSimplex3D(const Facet& f, const Point& z, const SimplexPhiMap& simplices);
+ ARSimplex3D(const Facet& f, const Point& z, const SimplexPhiMap& simplices,
+ const Delaunay& Dt);
ARSimplex3D(const Cell& c, const Point& z);
@@ -89,6 +90,8 @@
RealValue phi_const_; // see LHI paper, Appendices A and B
bool attached_;
+ // in paper's notation: s_ = v^2; v_ = d^2
+
};
typedef std::vector<ARSimplex3D> ARSimplex3DVector;
--- a/examples/ar-vineyard/ar-simplex3d.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/ar-vineyard/ar-simplex3d.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,3 +1,9 @@
+#include <utilities/log.h>
+
+#if LOGGING
+static rlog::RLogChannel* rlARSimplex3D = DEF_CHANNEL("ar/simplex3d", rlog::Log_Debug);
+#endif
+
ARSimplex3D::
ARSimplex3D(const ARSimplex3D& s): Parent(s)
{
@@ -35,19 +41,15 @@
}
ARSimplex3D::
-ARSimplex3D(const Edge& e, const Point& z, SimplexPhiMap& simplices, Facet_circulator facet_bg)
+ARSimplex3D(const Edge& e, const Point& z, SimplexPhiMap& simplices, const Delaunay& Dt, Facet_circulator facet_bg)
{
Cell_handle c = e.first;
Parent::add(c->vertex(e.second));
Parent::add(c->vertex(e.third));
Facet_circulator cur = facet_bg;
+ while (Dt.is_infinite(*cur)) ++cur;
SimplexPhiMap::const_iterator cur_iter = simplices.find(ARSimplex3D(*cur));
- while (cur_iter == simplices.end())
- {
- ++cur;
- cur_iter = simplices.find(ARSimplex3D(*cur));
- }
RealValue min = cur_iter->first.alpha();
RealValue phi_const_min = cur_iter->first.phi_const();
@@ -63,10 +65,8 @@
int i0 = (*cur).first->index(*v1);
int i1 = (*cur).first->index(*v2);
int i = 6 - i0 - i1 - (*cur).second;
- Point p3 = (*cur).first->vertex(i)->point();
- cur_iter = simplices.find(ARSimplex3D(*cur));
- if (cur_iter == simplices.end()) // cur is infinite
+ if (Dt.is_infinite(cur->first->vertex(i)))
{
++cur; continue;
// FIXME: what do we do with infinite cofaces (i.e., what
@@ -74,9 +74,11 @@
// infinite?
}
+ Point p3 = (*cur).first->vertex(i)->point();
if (CGAL::side_of_bounded_sphere(p1, p2, p3) == CGAL::ON_BOUNDED_SIDE)
attached_ = true;
+ SimplexPhiMap::const_iterator cur_iter = simplices.find(ARSimplex3D(*cur));
RealValue val = cur_iter->first.alpha();
if (val < min) min = val;
RealValue phi_const_val = cur_iter->first.phi_const();
@@ -99,7 +101,7 @@
s_ = CGAL::squared_distance(z, K::Segment_3(p1,p2).supporting_line());
- Point origin;
+ Point origin(0,0,0);
Point cc = origin + ((p1 - origin) + (p2 - origin))/2; // CGAL is funny
v_ = CGAL::squared_distance(z, cc) - s_;
}
@@ -114,7 +116,7 @@
}
ARSimplex3D::
-ARSimplex3D(const Facet& f, const Point& z, const SimplexPhiMap& simplices)
+ARSimplex3D(const Facet& f, const Point& z, const SimplexPhiMap& simplices, const Delaunay& Dt)
{
Cell_handle c = f.first;
for (int i = 0; i < 4; ++i)
@@ -133,29 +135,33 @@
rho_ = squared_radius(p1, p2, p3);
attached_ = false;
- if (CGAL::side_of_bounded_sphere(p1, p2, p3,
+ if (!Dt.is_infinite(c->vertex(f.second)) &&
+ CGAL::side_of_bounded_sphere(p1, p2, p3,
c->vertex(f.second)->point()) == CGAL::ON_BOUNDED_SIDE)
attached_ = true;
- else if (CGAL::side_of_bounded_sphere(p1, p2, p3,
+ else if (!Dt.is_infinite(o->vertex(oi)) &&
+ CGAL::side_of_bounded_sphere(p1, p2, p3,
o->vertex(oi)->point()) == CGAL::ON_BOUNDED_SIDE)
attached_ = true;
else
alpha_ = rho_;
- SimplexPhiMap::const_iterator c_iter = simplices.find(ARSimplex3D(*c,z));
- SimplexPhiMap::const_iterator o_iter = simplices.find(ARSimplex3D(*o,z));
- if (c_iter == simplices.end()) // c is infinite
+ if (Dt.is_infinite(c))
{
+ SimplexPhiMap::const_iterator o_iter = simplices.find(ARSimplex3D(*o,z));
if (attached_) alpha_ = o_iter->first.alpha();
phi_const_ = o_iter->first.phi_const(); // FIXME: it's probably the other way around
}
- else if (o_iter == simplices.end()) // o is infinite
+ else if (Dt.is_infinite(o))
{
+ SimplexPhiMap::const_iterator c_iter = simplices.find(ARSimplex3D(*c,z));
if (attached_) alpha_ = c_iter->first.alpha();
phi_const_ = c_iter->first.phi_const(); // FIXME: it's probably the other way around
}
else
{
+ SimplexPhiMap::const_iterator o_iter = simplices.find(ARSimplex3D(*o,z));
+ SimplexPhiMap::const_iterator c_iter = simplices.find(ARSimplex3D(*c,z));
if (attached_) alpha_ = std::min(c_iter->first.alpha(), o_iter->first.alpha());
phi_const_ = std::min(c_iter->first.phi_const(), o_iter->first.phi_const());
}
@@ -208,8 +214,9 @@
ARSimplex3D::
operator<<(std::ostream& out) const
{
+ out << this << ": ";
for (VertexSet::const_iterator cur = Parent::vertices().begin(); cur != Parent::vertices().end(); ++cur)
- out << **cur << ", ";
+ out << &(**cur) << ", ";
out << "value = " << value();
return out;
@@ -226,19 +233,19 @@
ARSimplex3D::SimplexPhiMap simplices;
for(Cell_iterator cur = Dt.finite_cells_begin(); cur != Dt.finite_cells_end(); ++cur)
update_simplex_phi_map(ARSimplex3D(*cur, z), simplices);
- std::cout << "Cells inserted" << std::endl;
+ rLog(rlARSimplex3D, "Cells inserted");
for(Vertex_iterator cur = Dt.finite_vertices_begin(); cur != Dt.finite_vertices_end(); ++cur)
simplices[ARSimplex3D(*cur, z)] = 0; // only one tetrahedron can have non-negative phi_const value
// (namely the one containing z); all other simplices will have a
// negative phi_const value, so 0 is safe
- std::cout << "Vertices inserted" << std::endl;
+ rLog(rlARSimplex3D, "Vertices inserted");
for(Facet_iterator cur = Dt.finite_facets_begin(); cur != Dt.finite_facets_end(); ++cur)
- update_simplex_phi_map(ARSimplex3D(*cur, z, simplices), simplices);
- std::cout << "Facets inserted" << std::endl;
+ update_simplex_phi_map(ARSimplex3D(*cur, z, simplices, Dt), simplices);
+ rLog(rlARSimplex3D, "Facets inserted");
for(Edge_iterator cur = Dt.finite_edges_begin(); cur != Dt.finite_edges_end(); ++cur)
- update_simplex_phi_map(ARSimplex3D(*cur, z, simplices, Dt.incident_facets(*cur)), simplices);
- std::cout << "Edges inserted" << std::endl;
+ update_simplex_phi_map(ARSimplex3D(*cur, z, simplices, Dt, Dt.incident_facets(*cur)), simplices);
+ rLog(rlARSimplex3D, "Edges inserted");
// Sort simplices by their alpha values
alpha_order.resize(simplices.size()); ARSimplex3DVector::iterator out = alpha_order.begin();
--- a/examples/ar-vineyard/ar-vineyard.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/ar-vineyard/ar-vineyard.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,40 +1,80 @@
-#include <utilities/sys.h>
-#include <utilities/debug.h>
+#include <utilities/log.h>
#include "ar-vineyard.h"
#include <iostream>
#include <fstream>
+#include <string>
+#include <vector>
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
int main(int argc, char** argv)
{
-#ifdef CWDEBUG
- Debug(dc::filtration.off());
- Debug(dc::cycle.off());
- Debug(dc::vineyard.off());
- Debug(dc::transpositions.off());
- Debug(dc::lsfiltration.off());
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ std::string infilename;
+ double zx,zy,zz,r;
+ std::string outfilename;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Points file")
+ ("x", po::value<double>(&zx), "x")
+ ("y", po::value<double>(&zy), "y")
+ ("z", po::value<double>(&zz), "z")
+ ("r", po::value<double>(&r), "r")
+ ("output-file", po::value<std::string>(&outfilename), "Vineyard edges output");
- dionysus::debug::init();
+ std::vector<std::string> log_channels;
+ po::options_description visible("Allowed options");
+ visible.add_options()
+ ("help,h", "produce help message");
+#if LOGGING
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels),
+ "log channels to turn on");
+#endif
+
+ po::positional_options_description p;
+ p.add("input-file", 1).add("x", 1).add("y", 1).add("z", 1).add("r", 1).add("output-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stdoutLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /* Interesting channels
+ * "ar/vineyard", "ar/function-kernel/value", "geometry/simulator",
+ * "topology/filtration", "topology/cycle", "topology/vineyard",
+ * "topology/filtration/transpositions", "topology/lowerstar"
+ */
#endif
// Read command-line arguments
- if (argc < 6)
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("output-file")
+ || !vm.count("x") || !vm.count("y") || !vm.count("z") || !vm.count("r"))
{
- std::cout << "Usage: ar-vineyard POINTS X Y Z OUTFILENAME" << std::endl;
+ std::cout << "Usage: ar-vineyard [OPTIONS] POINTS X Y Z R OUTFILENAME" << std::endl;
std::cout << " POINTS - filename containing points" << std::endl;
std::cout << " X,Y,Z - center-point z at which to compute the vineyard" << std::endl;
+ std::cout << " R - maximum radius" << std::endl;
std::cout << " OUTFILENAME - filename for the resulting vineyard" << std::endl;
+ std::cout << visible << std::endl;
std::cout << std::endl;
std::cout << "Computes an (alpha,r)-vineyard of the given pointset around the given point." << std::endl;
- exit(0);
+ return 1;
}
- std::string infilename = argv[1];
- double zx,zy,zz; std::istringstream(argv[2]) >> zx;
- std::istringstream(argv[3]) >> zy; std::istringstream(argv[4]) >> zz;
- std::string outfilename = argv[5];
-
// Read in the point set and compute its Delaunay triangulation
std::ifstream in(infilename.c_str());
@@ -52,7 +92,7 @@
arv.compute_pairing();
// Compute vineyard
- arv.compute_vineyard(true);
+ arv.compute_vineyard(r);
std::cout << "Vineyard computed" << std::endl;
arv.vineyard()->save_edges(outfilename);
}
--- a/examples/ar-vineyard/ar-vineyard.h Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/ar-vineyard/ar-vineyard.h Tue Jun 27 09:37:05 2017 -0700
@@ -6,84 +6,108 @@
#ifndef __AR_VINEYARD_H__
#define __AR_VINEYARD_H__
-#include "utilities/sys.h"
-#include "utilities/debug.h"
+#include <boost/signals.hpp>
+#include <boost/bind.hpp>
+#include <list>
+#include <vector>
#include "topology/conesimplex.h"
#include "topology/filtration.h"
#include "geometry/kinetic-sort.h"
+#include "geometry/simulator.h"
-#include <list>
#include "ar-simplex3d.h"
+#include "ar-function-kernel.h"
-class ARConeSimplex: public ConeSimplex<ARSimplex3D>
+template <class Simulator_>
+class ARConeSimplex3D: public ConeSimplex<ARSimplex3D>
{
public:
typedef ConeSimplex<ARSimplex3D> Parent;
typedef ARSimplex3D ARSimplex3D;
- typedef Filtration<ARConeSimplex> Filtration;
- /// \name Polynomial Kernel types
+ /// \name Simulator types
/// @{
- typedef double FieldType;
- typedef UPolynomial<FieldType> PolyKernel;
- typedef PolyKernel::Polynomial Polynomial;
- typedef Simulator<PolyKernel> Simulator;
+ typedef Simulator_ Simulator;
+ typedef typename Simulator::FunctionKernel FunctionKernel;
+ typedef typename FunctionKernel::Function Function;
+ /// @}
- typedef KineticSort<ARFiltration, SimplexTrajectoryExtractor, Simulator>
- SimplexSort;
- typedef SimplexSort::iterator SimplexSortIterator;
- typedef SimplexSortIterator Key;
+ /// \name ThresholdSort types
+ /// @{
+ typedef std::list<Function> ThresholdList;
+ typedef typename ThresholdList::iterator ThresholdListIterator;
+
+ struct ThresholdTrajectoryExtractor
+ { Function operator()(ThresholdListIterator i) const { return *i; } };
+
+ typedef KineticSort<ThresholdListIterator,
+ ThresholdTrajectoryExtractor, Simulator> ThresholdSort;
/// @}
- /// \name Kinetic Sort types
- /// @{
- typedef std::list<Polynomial> ThresholdList;
-
- struct ThresholdTrajectoryExtractor
- { Polynomial operator()(ThresholdList::iterator i) const { return *i; } }
- struct SimplexTrajectoryExtractor
- { Polynomial operator()(ARFiltration::iterator i) const { i->thresholds().front(); }
+ typedef boost::signal<void (Simulator*)> NewMaxSignal;
+
+ public:
+ ARConeSimplex3D(const ARSimplex3D& s, bool coned = false);
+ ARConeSimplex3D(const Parent& p): Parent(p) {} // crucial for boundary() to work correctly
+ ARConeSimplex3D(const ARConeSimplex3D& other): // need explicit copy-constructor because of the signal
+ Parent(other, other.coned()),
+ thresholds_(other.thresholds_) {}
- typedef KineticSort<ThresholdList, ThresholdTrajectoryExtractor, Simulator>
- ThresholdSort;
- /// @}
+ const ThresholdList& thresholds() const { return thresholds_; }
- ARConeSimplex(const ARSimplex3D& s, bool coned = false):
- Parent(s, coned),
- thresholds_sort_(&thresholds_) {}
+ NewMaxSignal& new_max_signal() { return new_max_signal_; }
+ const Function& max_threshold() const { return thresholds_.back(); }
+ void schedule_thresholds(Simulator* simulator);
- Key kinetic_key() const { return key_; }
- void set_kinetic_key(Key k) { key_ = k; }
- const ThresholdList& thresholds() const { return thresholds_; }
-
- void schedule_thresholds(Simulator* simulator);
+ // need explicit operator= because of the signal
+ ARConeSimplex3D& operator=(const ARConeSimplex3D& other) { Parent::operator=(other); thresholds_ = other.thresholds_; return *this; }
private:
- Key key_;
ThresholdList thresholds_;
ThresholdSort thresholds_sort_;
+ NewMaxSignal new_max_signal_;
- void swap_thresholds(ThresholdList* tl, typename ThresholdList::iterator i);
+ void swap_thresholds(ThresholdListIterator i, Simulator* simulator);
};
-
+/**
+ * Encapsulated filtration, and provides compute_vineyard() functionality.
+ */
class ARVineyard
{
public:
typedef ARVineyard Self;
+
+ /// \name FunctionKernel and Simulator types
+ /// @{
+ typedef ARFunctionKernel FunctionKernel;
+ typedef FunctionKernel::Function Function;
+ typedef Simulator<FunctionKernel> SimulatorAR;
+ /// @}
+
+ /// \name Filtration types
+ /// @{
+ typedef ARConeSimplex3D<SimulatorAR> ConeSimplex3D;
+ typedef Filtration<ConeSimplex3D> FiltrationAR;
+ typedef FiltrationAR::Simplex Simplex;
+ typedef FiltrationAR::Index Index;
+ typedef FiltrationAR::Vineyard Vineyard;
+ typedef Vineyard::Evaluator Evaluator;
+ /// @}
- typedef ARConeSimplex::Filtration ARFiltration;
- typedef ARFiltration::Simplex Simplex;
- typedef ARFiltration::Index Index;
- typedef ARFiltration::Vineyard Vineyard;
- typedef Vineyard::Evaluator Evaluator;
+ /// \name SimplexSort types
+ /// @{
+ struct SimplexTrajectoryExtractor
+ { Function operator()(Index i) const { return i->max_threshold(); } };
+
+ typedef KineticSort<Index, SimplexTrajectoryExtractor, SimulatorAR> SimplexSort;
+ typedef SimplexSort::iterator SimplexSortIterator;
- typedef ARConeSimplex::Simulator Simulator;
- typedef ARConeSimplex::SimplexSort SimplexSort;
-
+ class ThresholdChangeSlot; // used to notify of change in max threshold
+ /// @}
typedef std::list<Point> PointList;
@@ -95,21 +119,20 @@
~ARVineyard();
void compute_pairing();
- void compute_vineyard(bool explicit_events = false);
+ void compute_vineyard(double max_radius);
- const ARFiltration* filtration() const { return filtration_; }
+ const FiltrationAR* filtration() const { return filtration_; }
const Vineyard* vineyard() const { return vineyard_; }
public:
- // For Kinetic Sort
- static void swap(ARFiltration* filtration, Index i);
+ void swap(Index i, SimulatorAR* simulator); ///< For kinetic sort
private:
void add_simplices();
void change_evaluator(Evaluator* eval);
private:
- ARFiltration* filtration_;
+ FiltrationAR* filtration_;
Vineyard* vineyard_;
Evaluator* evaluator_;
@@ -133,35 +156,58 @@
//BOOST_CLASS_EXPORT(ARVineyard)
+#ifdef COUNTERS
+static Counter* cARVineyardTrajectoryKnee = GetCounter("ar/vineyard/trajectoryknee");
+#endif
+
+class ARVineyard::ThresholdChangeSlot
+{
+ public:
+ ThresholdChangeSlot(SimplexSortIterator iter, SimplexSort* sort,
+ Vineyard* vineyard, SimulatorAR* sort_simulator):
+ iter_(iter), sort_(sort), vineyard_(vineyard), sort_simulator_(sort_simulator) { iter_->element->new_max_signal().connect(*this); }
+ void operator()(SimulatorAR* simulator)
+ {
+ Count(cARVineyardTrajectoryKnee);
+ sort_->update_trajectory(iter_, sort_simulator_);
+ if (iter_->element->sign())
+ vineyard_->record_knee(iter_->element);
+ else
+ vineyard_->record_knee(iter_->element->pair());
+ }
+
+ private:
+ SimplexSortIterator iter_;
+ SimplexSort* sort_; // could make both of these static
+ Vineyard* vineyard_; // currently inefficient since there is
+ // only one SimplexSort and one Vineyard,
+ // but each is stored in every slot
+ SimulatorAR* sort_simulator_;
+};
class ARVineyard::StaticEvaluator: public Evaluator
{
public:
- StaticEvaluator(RealType t): time_(t) {}
+ StaticEvaluator() {}
- virtual RealType time() const { return time_; }
+ virtual RealType time() const { return 0; }
virtual RealType value(const Simplex& s) const { return s.value(); }
-
- private:
- RealType time_;
};
class ARVineyard::KineticEvaluator: public Evaluator
{
public:
- KineticEvaluator(Simulator::Handle sp,
- ActivePointsTable::Handle apt,
- RealType time_offset):
- sp_(sp), apt_(apt) {}
+ KineticEvaluator(SimulatorAR* simplex_sort_simulator,
+ SimulatorAR* trajectory_sort_simulator):
+ simplex_sort_simulator_(simplex_sort_simulator),
+ trajectory_sort_simulator_(trajectory_sort_simulator) {}
- virtual RealType time() const { return CGAL::to_double(get_time()); }
- virtual RealType value(const Simplex& s) const { return CGAL::to_double(apt_->at(s.kinetic_key()).x()(get_time())); }
+ virtual RealType time() const { return std::max(simplex_sort_simulator_->current_time(), trajectory_sort_simulator_->current_time()); }
+ virtual RealType value(const Simplex& s) const { return FunctionKernel::value_at(s.max_threshold(), time()); }
private:
- Simulator::Time get_time() const { return sp_->current_time(); }
-
- Simulator::Handle sp_;
- ActivePointsTable::Handle apt_;
+ SimulatorAR* simplex_sort_simulator_;
+ SimulatorAR* trajectory_sort_simulator_;
};
--- a/examples/ar-vineyard/ar-vineyard.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/ar-vineyard/ar-vineyard.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,44 +1,68 @@
+#include <utilities/log.h>
+
/* Implementation */
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlARVineyard = DEF_CHANNEL("ar/vineyard", rlog::Log_Debug);
+static rlog::RLogChannel* rlARVineyardComputing = DEF_CHANNEL("ar/vineyard/computing", rlog::Log_Debug);
+static rlog::RLogChannel* rlARVineyardSwap = DEF_CHANNEL("ar/vineyard/swap", rlog::Log_Debug);
+static rlog::RLogChannel* rlARVineyardThresholdSwap = DEF_CHANNEL("ar/vineyard/threshold/swap", rlog::Log_Debug);
+#endif
+
+
+template <class Simulator_>
+ARConeSimplex3D<Simulator_>::
+ARConeSimplex3D(const ARSimplex3D& s, bool coned): Parent(s, coned)
+{}
+template <class Simulator_>
void
-ARConeSimplex::
-swap_thresholds(ThresholdList* tl, typename ThresholdList::iterator i)
+ARConeSimplex3D<Simulator_>::
+swap_thresholds(ThresholdListIterator i, Simulator* simulator)
{
- AssertMsg(tl == &thresholds_, "Swap in the wrong list"); // might as well take advantage of the redundancy
-
+ rLog(rlARVineyardThresholdSwap, "Transposing %s and %s", tostring(*i).c_str(),
+ tostring(*boost::next(i)).c_str());
typename ThresholdList::iterator n = boost::next(i);
- tl->splice(i, *tl, n);
- if (n == tl->begin())
-
+ thresholds_.splice(i, thresholds_, n);
+ if (boost::next(i) == thresholds_.end())
+ new_max_signal_(simulator);
}
+template <class Simulator_>
void
-ARConeSimplex::
+ARConeSimplex3D<Simulator_>::
schedule_thresholds(Simulator* simulator)
{
- thresholds_sort_.insert(thresholds_sort_.end(), thresholds_.begin(), thresholds_.end(), simulator);
+ if (!coned()) thresholds_.push_back(Function(Function::rho, this));
+ else
+ {
+ thresholds_.push_back(Function(Function::phi, this));
+ thresholds_.push_back(Function(Function::rho, this));
+ thresholds_sort_.initialize(thresholds_.begin(), thresholds_.end(),
+ boost::bind(&ARConeSimplex3D::swap_thresholds, this, _1, _2), simulator);
+ }
}
ARVineyard::
-ARVineyard(const PointList& points, const Point& z): simplex_sort_(0), z_(z)
+ARVineyard(const PointList& points, const Point& z): z_(z)
{
for (PointList::const_iterator cur = points.begin(); cur != points.end(); ++cur)
dt_.insert(*cur);
- std::cout << "Delaunay triangulation computed" << std::endl;
+ rLog(rlARVineyard, "Delaunay triangulation computed");
ARSimplex3DVector alpha_ordering;
fill_alpha_order(dt_, z_, alpha_ordering);
- std::cout << "Delaunay simplices: " << alpha_ordering.size() << std::endl;
+ rLog(rlARVineyard, "Delaunay simplices: %i", alpha_ordering.size());
- evaluator_ = new StaticEvaluator(0);
+ evaluator_ = new StaticEvaluator;
vineyard_ = new Vineyard(evaluator_);
- filtration_ = new ARFiltration(vineyard_);
+ filtration_ = new FiltrationAR(vineyard_);
for (ARSimplex3DVector::const_iterator cur = alpha_ordering.begin(); cur != alpha_ordering.end(); ++cur)
{
- filtration_->append(ARConeSimplex(*cur)); // Delaunay simplex
- filtration_->append(ARConeSimplex(*cur, true)); // Coned off delaunay simplex
+ filtration_->append(ConeSimplex3D(*cur)); // Delaunay simplex
+ filtration_->append(ConeSimplex3D(*cur, true)); // Coned off delaunay simplex
}
}
@@ -57,111 +81,64 @@
filtration_->fill_simplex_index_map();
filtration_->pair_simplices(filtration_->begin(), filtration_->end());
vineyard_->start_vines(filtration_->begin(), filtration_->end());
- std::cout << "Simplices paired" << std::endl;
+ rLog(rlARVineyard, "Simplices paired");
}
void
ARVineyard::
-compute_vineyard(bool explicit_events)
+compute_vineyard(double max_radius)
{
AssertMsg(filtration_->is_paired(), "Simplices must be paired for a vineyard to be computed");
- Simulator simulator;
- SimplexSort simplex_sort(filtration_, swap);
-
-
-
-
-
-
-
- simplex_sort.initialize(&simulator);
-
-
-
- typedef Traits::Kinetic_kernel::Point_1 Point_1;
- typedef Simulator::Time Time;
+ SimulatorAR simplex_sort_simulator, trajectory_sort_simulator;
+ SimplexSort simplex_sort;
- Traits tr(0,1);
- Simulator::Handle sp = tr.simulator_handle();
- ActivePointsTable::Handle apt = tr.active_points_1_table_handle();
- Sort sort(tr, SortVisitor(this));
-
- // Setup the kinetic sort and membership changes
- std::cout << "Setting up the kinetic sort and membership events" << std::endl;
- CF cf;
- kinetic_map_.clear();
+ // Schedule thresholds
for (Index cur = filtration_->begin(); cur != filtration_->end(); ++cur)
- {
- F x = cf(F::NT(CGAL::to_double(cur->alpha())));
- Point_1 p(x);
- cur->set_kinetic_key(apt->insert(p));
- kinetic_map_[cur->kinetic_key()] = cur;
-
- if (!cur->coned()) continue; // non-coned simplices stay put, so we are done
+ cur->schedule_thresholds(&trajectory_sort_simulator);
- Time lambda_alpha = CGAL::to_double((cur->alpha() - cur->rho())); // when lambda becomes greater than alpha
- lambda_alpha += 2*CGAL::sqrt(CGAL::to_double(cur->s()*lambda_alpha));
- lambda_alpha += CGAL::to_double(cur->s() + cur->v());
-
- Time phi_alpha = CGAL::to_double(cur->alpha() - cur->phi_const());
+ // Once thresholds are scheduled, we can initialize the simplex_sort
+ simplex_sort.initialize(filtration_->begin(), filtration_->end(),
+ boost::bind(&ARVineyard::swap, this, _1, _2), &simplex_sort_simulator);
+ rLog(rlARVineyardComputing, "SimplexSort initialized");
- Time phi_lambda = CGAL::to_double(cur->rho() + cur->s() - cur->v() - cur->phi_const());
- phi_lambda *= phi_lambda;
- phi_lambda /= CGAL::to_double(4*cur->s());
- phi_lambda += CGAL::to_double(cur->v());
-
- Time sv = CGAL::to_double(cur->s() + cur->v());
-
- if (true || phi_lambda < sv || phi_lambda < phi_alpha) // FIXME: remove true
- {
- sp->new_event(Time(phi_alpha),
- MembershipFunctionChangeEvent(cur->kinetic_key(),
- cf(F::NT(CGAL::to_double(cur->phi_const())), 1),
- apt)); // \phi^2 = r^2 + \phi_c^2
- std::cout << "Scheduled" << std::endl;
- } else
- std::cout << "Not scheduled" << std::endl;
-
-
- //sp->new_event(Time(...), MembershipFunctionChangeEvent(cur->kinetic_key()));
-
- std::cout << *cur << std::endl;
- std::cout << "lambda_alpha: " << lambda_alpha << std::endl;
- std::cout << "phi_alpha: " << phi_alpha << std::endl;
- std::cout << "phi_lambda: " << phi_lambda << std::endl;
- std::cout << "s^2 + v^2: " << sv << std::endl;
- std::cout << std::endl;
- }
+ // Connect signals and slots
+ std::vector<ThresholdChangeSlot> slots;
+ slots.reserve(filtration_->size());
+ for (SimplexSortIterator cur = simplex_sort.begin(); cur != simplex_sort.end(); ++cur)
+ slots.push_back(ThresholdChangeSlot(cur, &simplex_sort, vineyard_, &simplex_sort_simulator));
+ rLog(rlARVineyardComputing, "Signals and slots connected");
+ rLog(rlARVineyardComputing, "SimplexSort size: %i", simplex_sort_simulator.size());
+ rLog(rlARVineyardComputing, "TrajectorySort size: %i", trajectory_sort_simulator.size());
- // Process all the events (compute the vineyard in the process)
- // FIXME: the time should not be 1, but something like twice the radius of
- // the pointset as seen from z
- change_evaluator(new KineticEvaluator(sp, apt, 0));
- if (explicit_events)
- {
- while (sp->next_event_time() < 1)
- {
- std::cout << "Next event time: " << sp->next_event_time() << std::endl;
- sp->set_current_event_number(sp->current_event_number() + 1);
- std::cout << "Processed event" << std::endl;
- }
- } else
- sp->set_current_time(1.0);
- std::cout << "Processed " << sp->current_event_number() << " events" << std::endl;
+ // Simulate
+ change_evaluator(new KineticEvaluator(&simplex_sort_simulator, &trajectory_sort_simulator));
+ while ((simplex_sort_simulator.current_time() < max_radius || trajectory_sort_simulator.current_time() < max_radius) && !(simplex_sort_simulator.reached_infinity() && trajectory_sort_simulator.reached_infinity()))
+ {
+ if (*(simplex_sort_simulator.top()) < *(trajectory_sort_simulator.top()))
+ {
+ rLog(rlARVineyardComputing, "Current time before: %lf (simplex sort)", simplex_sort_simulator.current_time());
+ rLog(rlARVineyardComputing, "Top event: %s (simplex sort)", intostring(*(simplex_sort_simulator.top())).c_str());
+ simplex_sort_simulator.process();
+ } else
+ {
+ rLog(rlARVineyardComputing, "Current time before: %lf (trajectory sort)", trajectory_sort_simulator.current_time());
+ rLog(rlARVineyardComputing, "Top event: %s (trajectory sort)", intostring(*(trajectory_sort_simulator.top())).c_str());
+ trajectory_sort_simulator.process();
+ }
+ }
- //change_evaluator(new StaticEvaluator(1));
vineyard_->record_diagram(filtration_->begin(), filtration_->end());
}
void
ARVineyard::
-swap(Key a, Key b)
+swap(Index i, SimulatorAR* simulator)
{
- Index ao = kinetic_map_[a], bo = kinetic_map_[b];
- AssertMsg(filtration_->get_trails_cmp()(ao, bo), "In swap(a,b), a must precede b");
- filtration_->transpose(ao);
- AssertMsg(filtration_->get_trails_cmp()(bo, ao), "In swap(a,b), b must precede a after the transposition");
+ rLog(rlARVineyardSwap, "Transposing %p and %p:", &(*i), &(*boost::next(i)));
+ rLog(rlARVineyardSwap, " %s and", tostring(*i).c_str());
+ rLog(rlARVineyardSwap, " %s", tostring(*boost::next(i)).c_str());
+ filtration_->transpose(i);
}
void
@@ -174,30 +151,3 @@
evaluator_ = eval;
vineyard_->set_evaluator(evaluator_);
}
-
-void
-ARVineyardBase::MembershipFunctionChangeEvent::
-process() const
-{
- apt_->set(key_, function_);
- std::cout << "Updated for phi's dominance" << std::endl;
-}
-
-
-template<class Vertex_handle>
-void
-ARVineyardBase::SortVisitor::
-before_swap(Vertex_handle a, Vertex_handle b) const
-{
- std::cout << "Swapping elements" << *a << " and " << *b << std::endl;
- arv_->swap(*a,*b);
-}
-
-
-std::ostream&
-ARVineyardBase::MembershipFunctionChangeEvent::
-operator<<(std::ostream& out) const
-{
- return out << "Membership change" << std::endl;
-}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/bottleneck-distance.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,68 @@
+#include <utilities/types.h>
+#include <topology/persistence-diagram.h>
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/serialization/map.hpp>
+
+#include <boost/program_options.hpp>
+
+
+typedef PersistenceDiagram<> PDgm;
+
+namespace po = boost::program_options;
+
+
+void read_diagram(PDgm& dgm, const std::string& filename)
+{
+ std::ifstream in(filename.c_str());
+ std::string line;
+ std::getline(in, line);
+ while (in)
+ {
+ std::istringstream sin(line);
+ double x,y;
+ sin >> x >> y;
+ dgm.push_back(PDgm::Point(x,y));
+ std::getline(in, line);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ std::string filename1, filename2;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file1", po::value<std::string>(&filename1), "The first collection of persistence diagrams")
+ ("input-file2", po::value<std::string>(&filename2), "The second collection of persistence diagrams");
+
+ po::positional_options_description p;
+ p.add("input-file1", 1);
+ p.add("input-file2", 2);
+
+ po::options_description all; all.add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+ if (!vm.count("input-file1") || !vm.count("input-file2"))
+ {
+ std::cout << "Usage: " << argv[0] << " input-file1 input-file2" << std::endl;
+ return 1;
+ }
+
+ PDgm dgm1, dgm2;
+ read_diagram(dgm1, filename1);
+ read_diagram(dgm2, filename2);
+ //std::cout << "Size dgm1: " << dgm1.size() << std::endl;
+ //std::cout << "Size dgm2: " << dgm2.size() << std::endl;
+
+ //std::cout << "Distance: " << bottleneck_distance(dgm1, dgm2) << std::endl;
+ std::cout << "L2-Distance: " << wasserstein_distance(dgm1, dgm2, 2) << std::endl;
+}
--- a/examples/cech-complex/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/cech-complex/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,7 +1,7 @@
-set (targets
- cech-complex)
-
-foreach (t ${targets})
- add_executable (${t} ${t}.cpp ${external_sources})
- target_link_libraries (${t} ${libraries} ${cgal_libraries})
-endforeach (t ${targets})
+set (targets
+ cech-complex)
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries})
+endforeach (t ${targets})
--- a/examples/cech-complex/cech-complex.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/cech-complex/cech-complex.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,8 +1,10 @@
-#include <utilities/sys.h>
-#include <utilities/debug.h>
+#include <utilities/log.h>
#include <topology/simplex.h>
#include <topology/filtration.h>
+#include <topology/static-persistence.h>
+//#include <topology/dynamic-persistence.h>
+#include <topology/persistence-diagram.h>
#include "Miniball_dynamic_d.h"
#include <algorithm>
@@ -10,120 +12,119 @@
#include <fstream>
-typedef std::vector<Point> PointContainer;
-typedef unsigned int PointIndex;
-typedef SimplexWithValue<PointIndex> Simplex;
-typedef std::vector<Simplex> SimplexVector;
-typedef Filtration<Simplex> CechFiltration;
-
-class DimensionValueComparison
-{
- public:
- bool operator()(const Simplex& s1, const Simplex& s2) const
- {
- if (s1.dimension() == s2.dimension())
- return s1.get_value() < s2.get_value();
- else
- return s1.dimension() < s2.dimension();
- }
-};
+typedef std::vector<Point> PointContainer;
+typedef unsigned int PointIndex;
+typedef Simplex<PointIndex, double> Smplx;
+typedef Filtration<Smplx> CechFiltration;
+typedef StaticPersistence<> Persistence;
+//typedef DynamicPersistenceTrails<> Persistence;
+typedef PersistenceDiagram<> PDgm;
int choose(int n, int k)
{
- if (k > n/2) k = n-k;
- int numerator = 1, denominator = 1;
- for (int i = 0; i < k; ++i)
- { numerator *= (n-i); denominator *= (i+1); }
- return numerator/denominator;
+ if (k > n/2) k = n-k;
+ int numerator = 1, denominator = 1;
+ for (int i = 0; i < k; ++i)
+ { numerator *= (n-i); denominator *= (i+1); }
+ return numerator/denominator;
}
-void add_simplices(SimplexVector& sv, int d, const PointContainer& points)
+void add_simplices(CechFiltration& sv, int d, const PointContainer& points)
{
- PointIndex indices[d+1];
- for (int i = 0; i < d+1; ++i)
- indices[i] = d - i;
+ PointIndex indices[d+1];
+ for (int i = 0; i < d+1; ++i)
+ indices[i] = d - i;
- while(indices[d] < points.size() - d)
- {
- // Add simplex
- Miniball mb(points[indices[0]].dim());
- Simplex s;
- for (int i = 0; i < d+1; ++i)
- {
- s.add(indices[i]);
- mb.check_in(points[indices[i]]);
- }
- mb.build();
- s.set_value(mb.squared_radius());
- sv.push_back(s);
+ while(indices[d] < points.size() - d)
+ {
+ // Add simplex
+ Miniball mb(points[indices[0]].dim());
+ Smplx s;
+ for (int i = 0; i < d+1; ++i)
+ {
+ s.add(indices[i]);
+ mb.check_in(points[indices[i]]);
+ }
+ mb.build();
+ s.data() = mb.squared_radius();
+ sv.push_back(s);
-
- // Advance indices
- for (int i = 0; i < d+1; ++i)
- {
- ++indices[i];
- if (indices[i] < points.size() - i)
- {
- for (int j = i-1; j >= 0; --j)
- indices[j] = indices[j+1] + 1;
- break;
- }
- }
- }
+
+ // Advance indices
+ for (int i = 0; i < d+1; ++i)
+ {
+ ++indices[i];
+ if (indices[i] < points.size() - i)
+ {
+ for (int j = i-1; j >= 0; --j)
+ indices[j] = indices[j+1] + 1;
+ break;
+ }
+ }
+ }
}
int main(int argc, char** argv)
{
- // Read in the point set and compute its Delaunay triangulation
- std::istream& in = std::cin;
- int ambient_d, homology_d;
- in >> ambient_d >> homology_d;
-
- std::cout << "Ambient dimension: " << ambient_d << std::endl;
- std::cout << "Will compute PD up to dimension: " << homology_d << std::endl;
-
- // Read points
- PointContainer points;
- while(in)
- {
- Point p(ambient_d);
- for (int i = 0; i < ambient_d; ++i)
- in >> p[i];
- points.push_back(p);
- }
- std::cout << "Points read: " << points.size() << std::endl;
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stdoutLog.subscribeTo( RLOG_CHANNEL("info") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ SetFrequency(GetCounter("persistence/pair"), 10000);
+ SetTrigger(GetCounter("persistence/pair"), GetCounter(""));
+
+ // Read in the point set and compute its Delaunay triangulation
+ std::istream& in = std::cin;
+ int ambient_d, homology_d;
+ in >> ambient_d >> homology_d;
+
+ rInfo("Ambient dimension: %d", ambient_d);
+ rInfo("Will compute PD up to dimension: %d", homology_d);
+
+ // Read points
+ PointContainer points;
+ while(in)
+ {
+ Point p(ambient_d);
+ for (int i = 0; i < ambient_d; ++i)
+ in >> p[i];
+ points.push_back(p);
+ }
+ rInfo("Points read: %d", points.size());
- // Compute Cech values
- CechFiltration cf;
- { // resource acquisition is initialization
- SimplexVector sv;
- int num_simplices = 0;
- for (int i = 0; i <= homology_d + 1; ++i)
- num_simplices += choose(points.size(), i+1);
- sv.reserve(num_simplices);
- std::cout << "Reserved SimplexVector of size: " << num_simplices << std::endl;
+ // Compute simplices with their Cech values
+ int num_simplices = 0;
+ for (int i = 0; i <= homology_d + 1; ++i)
+ num_simplices += choose(points.size(), i+1);
+ rInfo("Reserved SimplexVector of size: %d", num_simplices);
+
+ CechFiltration cf;
+ for (int i = 0; i <= homology_d + 1; ++i)
+ add_simplices(cf, i, points);
+ rInfo("Size of SimplexVector: %d", cf.size());
+
+ // Sort the filtration
+ cf.sort(DataDimensionComparison<Smplx>());
+ rInfo("Filtration initialized");
- for (int i = 0; i <= homology_d + 1; ++i)
- add_simplices(sv, i, points);
- std::cout << "Size of SimplexVector: " << sv.size() << std::endl;
-
- std::sort(sv.begin(), sv.end(), DimensionValueComparison());
-
- for (SimplexVector::const_iterator cur = sv.begin(); cur != sv.end(); ++cur)
- cf.append(*cur);
- }
+ // Compute persistence
+ Persistence p(cf);
+ rInfo("Persistence initialized");
+ p.pair_simplices();
+ rInfo("Simplices paired");
- // Compute persistence
- cf.fill_simplex_index_map();
- cf.pair_simplices(cf.begin(), cf.end());
- std::cout << "Simplices paired" << std::endl;
+ Persistence::SimplexMap<CechFiltration> m = p.make_simplex_map(cf);
+ std::map<Dimension, PDgm> dgms;
+ init_diagrams(dgms, p.begin(), p.end(),
+ evaluate_through_map(m, Smplx::DataEvaluator()),
+ evaluate_through_map(m, Smplx::DimensionExtractor()));
- for (CechFiltration::Index i = cf.begin(); i != cf.end(); ++i)
- if (i->is_paired())
- {
- if (i->sign())
- std::cout << i->dimension() << " " << i->get_value() << " " << i->pair()->get_value() << std::endl;
- } //else std::cout << i->value() << std::endl;
-
+ for (int i = 0; i <= homology_d; ++i)
+ {
+ std::cout << i << std::endl << dgms[i] << std::endl;
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,12 @@
+set (targets
+ rips-cohomology
+ rips-pairwise-cohomology
+ rips-explicit-cohomology
+ rips-weighted-cohomology
+ triangle-cohomology
+ )
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/cocycle.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+
+from cvxopt import spmatrix, matrix
+from cvxopt.blas import copy
+from lsqr import lsqr
+from sys import argv, exit
+import os.path
+
+def smooth(boundary_list, cocycle_list):
+ dimension = max((max(d[1], d[2]) for d in boundary_list))
+ dimension += 1
+
+ # NB: D is a coboundary matrix; 1 and 2 below are transposed
+ D = spmatrix([d[0] for d in boundary_list],
+ [d[2] for d in boundary_list],
+ [d[1] for d in boundary_list], (dimension, dimension))
+
+
+ z = spmatrix([zz[0] for zz in cocycle_list],
+ [zz[1] for zz in cocycle_list],
+ [0 for zz in cocycle_list], (dimension, 1))
+
+ v1 = D * z
+ # print "D^2 is zero:", not bool(D*D)
+ # print "D*z is zero:", not bool(v1)
+ z = matrix(z)
+
+ def Dfun(x,y,trans = 'N'):
+ if trans == 'N':
+ copy(D * x, y)
+ elif trans == 'T':
+ copy(D.T * x, y)
+ else:
+ assert False, "Unexpected trans parameter"
+
+ tol = 1e-10
+ show = False
+ maxit = None
+ solution = lsqr(Dfun, matrix(z), show = show, atol = tol, btol = tol, itnlim = maxit)
+
+ v = z - D*solution[0]
+
+ # print sum(v**2)
+ # assert sum((D*v)**2) < tol and sum((D.T*v)**2) < tol, "Expected a harmonic cocycle"
+ if not (sum((D*v)**2) < tol and sum((D.T*v)**2) < tol):
+ print "Expected a harmonic cocycle:", sum((D*v)**2), sum((D.T*v)**2)
+
+ return solution[0], v
+
+
+def vertex_values(solution, vertices):
+ values = [None]*len(vertices)
+ for i,v in vertices:
+ values[v] = solution[i]
+ return values
+
+
+def read_list_file(filename):
+ list = []
+ with open(filename) as fp:
+ for line in fp.xreadlines():
+ if line.startswith('#'): continue
+ list.append(map(int, line.split()))
+ return list
+
+
+if __name__ == '__main__':
+ if len(argv) < 4:
+ print "Usage: %s BOUNDARY COCYCLE VERTEXMAP" % argv[0]
+ exit()
+
+ boundary_filename = argv[1]
+ cocycle_filename = argv[2]
+ vertexmap_filename = argv[3]
+
+ boundary_list = read_list_file(boundary_filename)
+ cocycle_list = read_list_file(cocycle_filename)
+ vertexmap_list = read_list_file(vertexmap_filename)
+
+ solution, v = smooth(boundary_list, cocycle_list)
+ values = vertex_values(solution, vertexmap_list)
+
+ outfn = os.path.splitext(cocycle_filename)[0] + '.val'
+ with open(outfn, 'w') as f:
+ for i,v in enumerate(values):
+ f.write('%d %f\n' % (i,v))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/lsqr.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,408 @@
+# LSQR solver from http://pages.cs.wisc.edu/~kline/cvxopt/
+
+from cvxopt import matrix
+from cvxopt.lapack import *
+from cvxopt.blas import *
+from math import sqrt
+
+"""
+a,b are scalars
+
+On exit, returns scalars c,s,r
+"""
+def SymOrtho(a,b):
+ aa=abs(a)
+ ab=abs(b)
+ if b==0.:
+ s=0.
+ r=aa
+ if aa==0.:
+ c=1.
+ else:
+ c=a/aa
+ elif a==0.:
+ c=0.
+ s=b/ab
+ r=ab
+ elif ab>=aa:
+ sb=1
+ if b<0: sb=-1
+ tau=a/b
+ s=sb*(1+tau**2)**-0.5
+ c=s*tau
+ r=b/s
+ elif aa>ab:
+ sa=1
+ if a<0: sa=-1
+ tau=b/a
+ c=sa*(1+tau**2)**-0.5
+ s=c*tau
+ r=a/c
+
+ return c,s,r
+
+"""
+
+It is usually recommended to use SYMMLQ for symmetric matrices
+
+Requires the syntax
+ A(x,y) == y:=[A]*x
+and
+ A(x,y,trans='T') == y:=[A.T]*x
+
+comments with '###' are followed by the intent of the original matlab
+code. This may be useful for debugging.
+
+"""
+
+def lsqr( A, b, damp=0.0, atol=1e-8, btol=1e-8, conlim=1e8, itnlim=None, show=False, wantvar=False):
+ """
+
+ [ x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var ]...
+ = lsqr( m, n, 'aprod', iw, rw, b, damp, atol, btol, conlim, itnlim, show );
+
+ LSQR solves Ax = b or min ||b - Ax||_2 if damp = 0,
+ or min || (b) - ( A )x || otherwise.
+ || (0) (damp I) ||2
+ A is an m by n matrix defined by y = aprod( mode,m,n,x,iw,rw ),
+ where the parameter 'aprodname' refers to a function 'aprod' that
+ performs the matrix-vector operations.
+ If mode = 1, aprod must return y = Ax without altering x.
+ If mode = 2, aprod must return y = A'x without altering x.
+ WARNING: The file containing the function 'aprod'
+ must not be called aprodname.m !!!!
+
+ -----------------------------------------------------------------------
+ LSQR uses an iterative (conjugate-gradient-like) method.
+ For further information, see
+ 1. C. C. Paige and M. A. Saunders (1982a).
+ LSQR: An algorithm for sparse linear equations and sparse least squares,
+ ACM TOMS 8(1), 43-71.
+ 2. C. C. Paige and M. A. Saunders (1982b).
+ Algorithm 583. LSQR: Sparse linear equations and least squares problems,
+ ACM TOMS 8(2), 195-209.
+ 3. M. A. Saunders (1995). Solution of sparse rectangular systems using
+ LSQR and CRAIG, BIT 35, 588-604.
+
+ Input parameters:
+ iw, rw are not used by lsqr, but are passed to aprod.
+ atol, btol are stopping tolerances. If both are 1.0e-9 (say),
+ the final residual norm should be accurate to about 9 digits.
+ (The final x will usually have fewer correct digits,
+ depending on cond(A) and the size of damp.)
+ conlim is also a stopping tolerance. lsqr terminates if an estimate
+ of cond(A) exceeds conlim. For compatible systems Ax = b,
+ conlim could be as large as 1.0e+12 (say). For least-squares
+ problems, conlim should be less than 1.0e+8.
+ Maximum precision can be obtained by setting
+ atol = btol = conlim = zero, but the number of iterations
+ may then be excessive.
+ itnlim is an explicit limit on iterations (for safety).
+ show = 1 gives an iteration log,
+ show = 0 suppresses output.
+
+ Output parameters:
+ x is the final solution.
+ istop gives the reason for termination.
+ istop = 1 means x is an approximate solution to Ax = b.
+ = 2 means x approximately solves the least-squares problem.
+ r1norm = norm(r), where r = b - Ax.
+ r2norm = sqrt( norm(r)^2 + damp^2 * norm(x)^2 )
+ = r1norm if damp = 0.
+ anorm = estimate of Frobenius norm of Abar = [ A ].
+ [damp*I]
+ acond = estimate of cond(Abar).
+ arnorm = estimate of norm(A'*r - damp^2*x).
+ xnorm = norm(x).
+ var (if present) estimates all diagonals of (A'A)^{-1} (if damp=0)
+ or more generally (A'A + damp^2*I)^{-1}.
+ This is well defined if A has full column rank or damp > 0.
+ (Not sure what var means if rank(A) < n and damp = 0.)
+
+
+ 1990: Derived from Fortran 77 version of LSQR.
+ 22 May 1992: bbnorm was used incorrectly. Replaced by anorm.
+ 26 Oct 1992: More input and output parameters added.
+ 01 Sep 1994: Matrix-vector routine is now a parameter 'aprodname'.
+ Print log reformatted.
+ 14 Jun 1997: show added to allow printing or not.
+ 30 Jun 1997: var added as an optional output parameter.
+ 07 Aug 2002: Output parameter rnorm replaced by r1norm and r2norm.
+ Michael Saunders, Systems Optimization Laboratory,
+ Dept of MS&E, Stanford University.
+ -----------------------------------------------------------------------
+ """
+ """
+ Initialize.
+ """
+ n=len(b)
+ m=n
+ if itnlim is None: itnlim=2*n
+
+ msg=('The exact solution is x = 0 ',
+ 'Ax - b is small enough, given atol, btol ',
+ 'The least-squares solution is good enough, given atol ',
+ 'The estimate of cond(Abar) has exceeded conlim ',
+ 'Ax - b is small enough for this machine ',
+ 'The least-squares solution is good enough for this machine',
+ 'Cond(Abar) seems to be too large for this machine ',
+ 'The iteration limit has been reached ');
+
+ var = matrix(0.,(n,1));
+
+ if show:
+ print ' '
+ print 'LSQR Least-squares solution of Ax = b'
+ str1 = 'The matrix A has %8g rows and %8g cols' % (m, n)
+ str2 = 'damp = %20.14e wantvar = %8g' %( damp,wantvar)
+ str3 = 'atol = %8.2e conlim = %8.2e'%( atol, conlim)
+ str4 = 'btol = %8.2e itnlim = %8g' %( btol, itnlim)
+ print str1
+ print str2
+ print str3
+ print str4
+
+ itn = 0; istop = 0; nstop = 0;
+ ctol = 0;
+ if conlim > 0: ctol = 1/conlim
+ anorm = 0; acond = 0;
+ dampsq = damp**2; ddnorm = 0; res2 = 0;
+ xnorm = 0; xxnorm = 0; z = 0;
+ cs2 = -1; sn2 = 0;
+
+ """
+ Set up the first vectors u and v for the bidiagonalization.
+ These satisfy beta*u = b, alfa*v = A'u.
+ """
+ __x = matrix(0., (n,1)) # a matrix for temporary holding
+ v = matrix(0., (n,1))
+ u = +b;
+ x = matrix(0., (n,1))
+ alfa = 0;
+ beta = nrm2( u );
+ w = matrix(0., (n,1))
+
+ if beta > 0:
+ ### u = (1/beta) * u;
+ ### v = feval( aprodname, 2, m, n, u, iw, rw );
+ scal(1/beta,u)
+ A(u,v,trans='T'); #v = feval( aprodname, 2, m, n, u, iw, rw );
+ alfa = nrm2( v );
+
+ if alfa > 0:
+ ### v = (1/alfa) * v;
+ scal(1/alfa,v)
+ copy(v,w)
+
+
+ rhobar = alfa; phibar = beta; bnorm = beta;
+ rnorm = beta;
+ r1norm = rnorm;
+ r2norm = rnorm;
+
+ # reverse the order here from the original matlab code because
+ # there was an error on return when arnorm==0
+ arnorm = alfa * beta;
+ if arnorm == 0:
+ print msg[0];
+ return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var
+
+ head1 = ' Itn x[0] r1norm r2norm ';
+ head2 = ' Compatible LS Norm A Cond A';
+
+ if show:
+ print ' '
+ print head1, head2
+ test1 = 1; test2 = alfa / beta;
+ str1 = '%6g %12.5e' %( itn, x[0] );
+ str2 = ' %10.3e %10.3e'%( r1norm, r2norm );
+ str3 = ' %8.1e %8.1e' %( test1, test2 );
+ print str1, str2, str3
+
+ """
+ %------------------------------------------------------------------
+ % Main iteration loop.
+ %------------------------------------------------------------------
+ """
+ while itn < itnlim:
+ itn = itn + 1;
+ """
+ % Perform the next step of the bidiagonalization to obtain the
+ % next beta, u, alfa, v. These satisfy the relations
+ % beta*u = a*v - alfa*u,
+ % alfa*v = A'*u - beta*v.
+ """
+ ### u = feval( aprodname, 1, m, n, v, iw, rw ) - alfa*u;
+ copy(u, __x)
+ A(v,u)
+ axpy(__x,u,-alfa)
+
+ beta = nrm2( u );
+ if beta > 0:
+ ### u = (1/beta) * u;
+ scal(1/beta,u)
+ anorm = sqrt(anorm**2 + alfa**2 + beta**2 + damp**2);
+ ### v = feval( aprodname, 2, m, n, u, iw, rw ) - beta*v;
+ copy(v,__x)
+ A(u,v,trans='T')
+ axpy(__x,v,-beta)
+
+ alfa = nrm2( v );
+ if alfa > 0:
+ ### v = (1/alfa) * v;
+ scal(1/alfa, v)
+
+ """
+ % Use a plane rotation to eliminate the damping parameter.
+ % This alters the diagonal (rhobar) of the lower-bidiagonal matrix.
+ """
+
+ rhobar1 = sqrt(rhobar**2 + damp**2);
+ cs1 = rhobar / rhobar1;
+ sn1 = damp / rhobar1;
+ psi = sn1 * phibar;
+ phibar = cs1 * phibar;
+ """
+ % Use a plane rotation to eliminate the subdiagonal element (beta)
+ % of the lower-bidiagonal matrix, giving an upper-bidiagonal matrix.
+ """
+
+
+ ###cs = rhobar1/ rho;
+ ###sn = beta / rho;
+ cs,sn,rho = SymOrtho(rhobar1,beta)
+
+ theta = sn * alfa;
+ rhobar = - cs * alfa;
+ phi = cs * phibar;
+ phibar = sn * phibar;
+ tau = sn * phi;
+ """
+ % Update x and w.
+ """
+ t1 = phi /rho;
+ t2 = - theta/rho;
+ dk = (1/rho)*w;
+
+ ### x = x + t1*w;
+ axpy(w,x,t1)
+ ### w = v + t2*w;
+ scal(t2,w)
+ axpy(v,w)
+ ddnorm = ddnorm + nrm2(dk)**2;
+ if wantvar:
+ ### var = var + dk.*dk;
+ axpy(dk**2, var)
+ """
+ % Use a plane rotation on the right to eliminate the
+ % super-diagonal element (theta) of the upper-bidiagonal matrix.
+ % Then use the result to estimate norm(x).
+ """
+
+ delta = sn2 * rho;
+ gambar = - cs2 * rho;
+ rhs = phi - delta * z;
+ zbar = rhs / gambar;
+ xnorm = sqrt(xxnorm + zbar**2);
+ gamma = sqrt(gambar**2 +theta**2);
+ cs2 = gambar / gamma;
+ sn2 = theta / gamma;
+ z = rhs / gamma;
+ xxnorm = xxnorm + z**2;
+ """
+ % Test for convergence.
+ % First, estimate the condition of the matrix Abar,
+ % and the norms of rbar and Abar'rbar.
+ """
+ acond = anorm * sqrt(ddnorm);
+ res1 = phibar**2;
+ res2 = res2 + psi**2;
+ rnorm = sqrt( res1 + res2 );
+ arnorm = alfa * abs( tau );
+ """
+ % 07 Aug 2002:
+ % Distinguish between
+ % r1norm = ||b - Ax|| and
+ % r2norm = rnorm in current code
+ % = sqrt(r1norm^2 + damp^2*||x||^2).
+ % Estimate r1norm from
+ % r1norm = sqrt(r2norm^2 - damp^2*||x||^2).
+ % Although there is cancellation, it might be accurate enough.
+ """
+ r1sq = rnorm**2 - dampsq * xxnorm;
+ r1norm = sqrt( abs(r1sq) );
+ if r1sq < 0: r1norm = - r1norm;
+ r2norm = rnorm;
+ """
+ % Now use these norms to estimate certain other quantities,
+ % some of which will be small near a solution.
+ """
+ test1 = rnorm / bnorm;
+ test2 = arnorm/( anorm * rnorm );
+ test3 = 1 / acond;
+ t1 = test1 / (1 + anorm * xnorm / bnorm);
+ rtol = btol + atol * anorm * xnorm / bnorm;
+ """
+ % The following tests guard against extremely small values of
+ % atol, btol or ctol. (The user may have set any or all of
+ % the parameters atol, btol, conlim to 0.)
+ % The effect is equivalent to the normal tests using
+ % atol = eps, btol = eps, conlim = 1/eps.
+ """
+ if itn >= itnlim : istop = 7;
+ if 1 + test3 <= 1: istop = 6;
+ if 1 + test2 <= 1: istop = 5;
+ if 1 + t1 <= 1: istop = 4;
+ """
+ % Allow for tolerances set by the user.
+ """
+ if test3 <= ctol: istop = 3;
+ if test2 <= atol: istop = 2;
+ if test1 <= rtol: istop = 1;
+ """
+ % See if it is time to print something.
+ """
+ prnt = False;
+ if n <= 40 : prnt = True;
+ if itn <= 10 : prnt = True;
+ if itn >= itnlim-10: prnt = True;
+ # if itn%10 == 0 : prnt = True;
+ if test3 <= 2*ctol : prnt = True;
+ if test2 <= 10*atol : prnt = True;
+ if test1 <= 10*rtol : prnt = True;
+ if istop != 0 : prnt = True;
+
+ if prnt:
+ if show:
+ str1 = '%6g %12.5e'% (itn, x[0] );
+ str2 = ' %10.3e %10.3e'% (r1norm, r2norm );
+ str3 = ' %8.1e %8.1e'% ( test1, test2 );
+ str4 = ' %8.1e %8.1e'% ( anorm, acond );
+ print str1, str2, str3, str4
+
+ if istop != 0: break
+
+ """
+ % End of iteration loop.
+ % Print the stopping condition.
+ """
+ if show:
+ print ' '
+ print 'LSQR finished'
+ print msg[istop]
+ print ' '
+ str1 = 'istop =%8g r1norm =%8.1e'% ( istop, r1norm );
+ str2 = 'anorm =%8.1e arnorm =%8.1e'%( anorm, arnorm );
+ str3 = 'itn =%8g r2norm =%8.1e'% ( itn, r2norm );
+ str4 = 'acond =%8.1e xnorm =%8.1e'%( acond, xnorm );
+ print str1+ ' ' +str2
+ print str3+ ' ' +str4
+ print ' '
+
+ return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var
+
+ """
+ %-----------------------------------------------------------------------
+ % End of lsqr.m
+ %-----------------------------------------------------------------------
+ """
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/output.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,66 @@
+#include <iostream>
+#include <sstream>
+#include <fstream>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+
+#include <cassert>
+
+bool neq(const Smplx& s1, const Smplx& s2)
+{
+ Smplx::VertexComparison cmp;
+ return cmp(s1, s2) || cmp(s2, s1);
+}
+
+template<class Comparison>
+unsigned index(const SimplexVector& v, const Smplx& s, const Comparison& cmp)
+{
+ SimplexVector::const_iterator it = std::lower_bound(v.begin(), v.end(), s, cmp);
+ while (neq(*it, s)) ++it;
+ return it - v.begin();
+}
+
+template<class Comparison>
+void output_boundary_matrix(std::ostream& out, const SimplexVector& v, const Comparison& cmp)
+{
+ unsigned i = 0;
+ for (SimplexVector::const_iterator cur = v.begin(); cur != v.end(); ++cur)
+ {
+ // std::cout << "Simplex: " << *cur << std::endl;
+ bool sign = true;
+ for (Smplx::BoundaryIterator bcur = cur->boundary_begin(); bcur != cur->boundary_end(); ++bcur)
+ {
+ // std::cout << " " << *bcur << std::endl;
+ out << (sign ? 1 : -1) << " ";
+ out << index(v, *bcur, cmp) << " " << i << "\n";
+ sign = !sign;
+ }
+ ++i;
+ }
+}
+
+void output_vertex_indices(std::ostream& out, const SimplexVector& v)
+{
+ unsigned i = 0;
+ for (SimplexVector::const_iterator cur = v.begin(); cur != v.end(); ++cur)
+ {
+ if (cur->dimension() == 0)
+ out << i << " " << cur->vertices()[0] << std::endl;
+ ++i;
+ }
+}
+
+void output_cocycle(std::string cocycle_prefix, unsigned i, const SimplexVector& v, const BirthInfo& birth, const Persistence::ZColumn& zcol, ZpField::Element prime)
+{
+ std::ostringstream istr; istr << '-' << i;
+ std::string filename = cocycle_prefix + istr.str() + ".ccl";
+ std::ofstream out(filename.c_str());
+ out << "# Cocycle born at " << birth.get<1>() << std::endl;
+ for (Persistence::ZColumn::const_iterator zcur = zcol.begin(); zcur != zcol.end(); ++zcur)
+ {
+ //const Smplx& s = **(zcur->si);
+ out << (zcur->coefficient <= prime/2 ? zcur->coefficient : zcur->coefficient - prime) << " ";
+ out << zcur->si->getValue() << "\n";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/rips-cohomology.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,62 @@
+#include <topology/cohomology-persistence.h>
+#include <topology/rips.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+
+#include <map>
+#include <iostream>
+
+#include <boost/tuple/tuple.hpp>
+
+// Trivial example of size() points on a line with integer coordinates
+struct Distances
+{
+ typedef int IndexType;
+ typedef double DistanceType;
+
+ DistanceType operator()(IndexType a, IndexType b) const { return std::abs(a - b); }
+
+ size_t size() const { return 2000; }
+ IndexType begin() const { return 0; }
+ IndexType end() const { return size(); }
+};
+
+typedef CohomologyPersistence<Distances::DistanceType> Persistence;
+typedef Persistence::SimplexIndex Index;
+typedef Persistence::Death Death;
+typedef Persistence::CocyclePtr CocyclePtr;
+
+typedef Rips<Distances> Generator;
+typedef Generator::Simplex Smplx;
+
+typedef std::map<Smplx, Index,
+ Smplx::VertexComparison> Complex;
+typedef std::vector<Smplx> SimplexVector;
+
+int main()
+{
+ Distances distances;
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ SimplexVector v;
+ Complex c;
+
+ rips.generate(2, 50, make_push_back_functor(v));
+ std::sort(v.begin(), v.end(), Generator::Comparison(distances));
+ std::cout << "Simplex vector generated, size: " << v.size() << std::endl;
+
+ Persistence p;
+ for (SimplexVector::const_iterator cur = v.begin(); cur != v.end(); ++cur)
+ {
+ std::vector<Index> boundary;
+ for (Smplx::BoundaryIterator bcur = cur->boundary_begin();
+ bcur != cur->boundary_end(); ++bcur)
+ boundary.push_back(c[*bcur]);
+
+ Index idx; Death d; CocyclePtr ccl;
+ boost::tie(idx, d, ccl) = p.add(boundary.begin(), boundary.end(), size(*cur));
+ c[*cur] = idx;
+ if (d && (size(*cur) - *d) > 0)
+ std::cout << (cur->dimension() - 1) << " " << *d << " " << size(*cur) << std::endl;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/rips-explicit-cohomology.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,198 @@
+#include <topology/cohomology-persistence.h>
+#include <topology/rips.h>
+
+#include <geometry/l2distance.h>
+#include <geometry/distances.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+#include <utilities/property-maps.h>
+#include <utilities/timer.h>
+#include <utilities/log.h>
+#include <utilities/counter.h>
+#include <utilities/memory.h>
+
+#include <string>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#include "wrappers.h"
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+typedef PairDistances::IndexType Vertex;
+
+typedef boost::tuple<Dimension, DistanceType> BirthInfo;
+typedef CohomologyPersistence<BirthInfo, Wrapper<unsigned> > Persistence;
+typedef Persistence::SimplexIndex Index;
+typedef Persistence::Death Death;
+typedef Persistence::CocyclePtr CocyclePtr;
+
+typedef ExplicitDistances< PairDistances > Distances;
+typedef Rips<Distances, Simplex<Vertex, Index> > Generator;
+typedef Generator::Simplex Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef SimplexVector::const_iterator SV_const_iterator;
+
+typedef std::map<Smplx, Index, Smplx::VertexComparison> Complex;
+
+#include "output.h" // for output_*()
+
+void program_options(int argc, char* argv[], std::string& infilename, unsigned& pt_size, Dimension& skeleton, DistanceType& max_distance, ZpField::Element& prime, std::string& boundary_name, std::string& cocycle_prefix, std::string& vertices_name, std::string& diagram_name);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Dimension skeleton;
+ DistanceType max_distance;
+ ZpField::Element prime;
+ unsigned pt_size;
+ std::string infilename, boundary_name, cocycle_prefix, vertices_name, diagram_name;
+
+ program_options(argc, argv, infilename, pt_size, skeleton, max_distance, prime, boundary_name, cocycle_prefix, vertices_name, diagram_name);
+ std::ofstream bdry_out(boundary_name.c_str());
+ std::ofstream vertices_out(vertices_name.c_str());
+ std::ofstream diagram_out(diagram_name.c_str());
+ std::cout << "# of points: " << pt_size << std::endl;
+ std::cout << "Boundary matrix: " << boundary_name << std::endl;
+ std::cout << "Cocycles: " << cocycle_prefix << "*.ccl" << std::endl;
+ std::cout << "Vertices: " << vertices_name << std::endl;
+ std::cout << "Diagram: " << diagram_name << std::endl;
+
+ Timer total_timer; total_timer.start();
+
+ // Read the pairwise distances
+ std::ifstream in(infilename.c_str());
+ Distances distances(pt_size);
+ for (unsigned i = 0; i < pt_size; ++i)
+ for (unsigned j = 0; j < pt_size; ++j)
+ in >> distances(i,j);
+
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ Generator::Comparison cmp(distances);
+ SimplexVector v;
+
+ Timer rips_timer; rips_timer.start();
+ rips.generate(skeleton, max_distance, make_push_back_functor(v));
+ std::sort(v.begin(), v.end(), Smplx::VertexComparison());
+
+ std::vector<unsigned> index_in_v(v.size());
+ for (unsigned idx = 0; idx < v.size(); ++idx)
+ index_in_v[idx] = idx;
+ std::sort(index_in_v.begin(), index_in_v.end(), IndirectIndexComparison<SimplexVector, Generator::Comparison>(v, cmp));
+
+ BinarySearchMap<Smplx, SimplexVector::iterator, Smplx::VertexComparison> map_of_v(v.begin(), v.end());
+
+ rips_timer.stop();
+ std::cout << "Simplex vector generated, size: " << v.size() << std::endl;
+
+ output_boundary_matrix(bdry_out, v, Smplx::VertexComparison());
+ output_vertex_indices(vertices_out, v);
+
+ Timer persistence_timer; persistence_timer.start();
+ ZpField zp(prime);
+ Persistence p(zp);
+ boost::progress_display show_progress(v.size());
+
+ for (unsigned j = 0; j < index_in_v.size(); ++j)
+ {
+ SimplexVector::const_iterator cur = v.begin() + index_in_v[j];
+ std::vector<Index> boundary;
+ for (Smplx::BoundaryIterator bcur = cur->boundary_begin(); bcur != cur->boundary_end(); ++bcur)
+ boundary.push_back(map_of_v[*bcur]->data());
+
+ Index idx; Death d; CocyclePtr ccl;
+ bool store = cur->dimension() < skeleton;
+ boost::tie(idx, d, ccl) = p.add(boundary.begin(), boundary.end(), boost::make_tuple(cur->dimension(), size(*cur)), store, index_in_v[j]);
+
+ // c[*cur] = idx;
+ if (store)
+ map_of_v[*cur]->data() = idx;
+
+ if (d && (size(*cur) - d->get<1>()) > 0)
+ {
+ AssertMsg(d->get<0>() == cur->dimension() - 1, "Dimensions must match");
+ diagram_out << (cur->dimension() - 1) << " " << d->get<1>() << " " << size(*cur) << std::endl;
+ }
+ ++show_progress;
+ }
+ // output infinte persistence pairs
+ for (Persistence::CocycleIndex cur = p.begin(); cur != p.end(); ++cur)
+ diagram_out << cur->birth.get<0>() << " " << cur->birth.get<1>() << " inf" << std::endl;
+ persistence_timer.stop();
+
+
+ // p.show_cocycles();
+ // Output alive cocycles of dimension 1
+ if (!cocycle_prefix.empty())
+ {
+ unsigned i = 0;
+ for (Persistence::Cocycles::const_iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ if (cur->birth.get<0>() != 1) continue;
+ output_cocycle(cocycle_prefix, i, v, cur->birth, cur->zcolumn, prime);
+ // std::cout << "Cocycle of dimension: " << cur->birth.get<0>() << " born at " << cur->birth.get<1>() << std::endl;
+ ++i;
+ }
+ }
+ total_timer.stop();
+ rips_timer.check("Rips timer");
+ persistence_timer.check("Persistence timer");
+ total_timer.check("Total timer");
+}
+
+void program_options(int argc, char* argv[], std::string& infilename, unsigned& pt_size, Dimension& skeleton, DistanceType& max_distance, ZpField::Element& prime, std::string& boundary_name, std::string& cocycle_prefix, std::string& vertices_name, std::string& diagram_name)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute")
+ ("num-pts", po::value<unsigned>(&pt_size), "Number of points in the input");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<Dimension>(&skeleton)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("prime,p", po::value<ZpField::Element>(&prime)->default_value(11), "Prime p for the field F_p")
+ ("max-distance,m", po::value<DistanceType>(&max_distance)->default_value(Infinity), "Maximum value for the Rips complex construction")
+ ("boundary,b", po::value<std::string>(&boundary_name), "Filename where to output the boundary matrix")
+ ("cocycle,c", po::value<std::string>(&cocycle_prefix), "Prefix of the filename where to output the 1-dimensional cocycles")
+ ("vertices,v", po::value<std::string>(&vertices_name), "Filename where to output the simplex-vertex mapping")
+ ("diagram,d", po::value<std::string>(&diagram_name), "Filename where to output the persistence diagram");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("num-pts", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("input-file")|| !vm.count("num-pts"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file num-pts" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/rips-pairwise-cohomology.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,209 @@
+#include <topology/cohomology-persistence.h>
+#include <topology/rips.h>
+
+#include <geometry/l2distance.h>
+#include <geometry/distances.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+#include <utilities/property-maps.h>
+#include <utilities/timer.h>
+#include <utilities/log.h>
+#include <utilities/counter.h>
+#include <utilities/memory.h>
+
+#include <string>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#include "wrappers.h"
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+typedef PairDistances::IndexType Vertex;
+
+typedef boost::tuple<Dimension, DistanceType> BirthInfo;
+typedef CohomologyPersistence<BirthInfo, Wrapper<unsigned> > Persistence;
+typedef Persistence::SimplexIndex Index;
+typedef Persistence::Death Death;
+typedef Persistence::CocyclePtr CocyclePtr;
+
+typedef Rips<PairDistances, Simplex<Vertex, Index> > Generator;
+typedef Generator::Simplex Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef SimplexVector::const_iterator SV_const_iterator;
+
+typedef std::map<Smplx, Index, Smplx::VertexComparison> Complex;
+
+#include "output.h" // for output_*()
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, ZpField::Element& prime, std::string& boundary_name, std::string& cocycle_prefix, std::string& vertices_name, std::string& diagram_name);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Dimension skeleton;
+ DistanceType max_distance;
+ ZpField::Element prime;
+ std::string infilename, boundary_name, cocycle_prefix, vertices_name, diagram_name;
+
+ program_options(argc, argv, infilename, skeleton, max_distance, prime, boundary_name, cocycle_prefix, vertices_name, diagram_name);
+ std::ofstream bdry_out(boundary_name.c_str());
+ std::ofstream vertices_out(vertices_name.c_str());
+ std::ofstream diagram_out(diagram_name.c_str());
+ std::cout << "Boundary matrix: " << boundary_name << std::endl;
+ std::cout << "Cocycles: " << cocycle_prefix << "*.ccl" << std::endl;
+ std::cout << "Vertices: " << vertices_name << std::endl;
+ std::cout << "Diagram: " << diagram_name << std::endl;
+
+ Timer total_timer; total_timer.start();
+ PointContainer points;
+ read_points(infilename, points);
+
+ PairDistances distances(points);
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ Generator::Comparison cmp(distances);
+ SimplexVector v;
+
+ Timer rips_timer; rips_timer.start();
+ rips.generate(skeleton, max_distance, make_push_back_functor(v));
+ std::sort(v.begin(), v.end(), Smplx::VertexComparison());
+
+ std::vector<unsigned> index_in_v(v.size());
+ for (unsigned idx = 0; idx < v.size(); ++idx)
+ index_in_v[idx] = idx;
+ std::sort(index_in_v.begin(), index_in_v.end(), IndirectIndexComparison<SimplexVector, Generator::Comparison>(v, cmp));
+
+ BinarySearchMap<Smplx, SimplexVector::iterator, Smplx::VertexComparison> map_of_v(v.begin(), v.end());
+
+ rips_timer.stop();
+ std::cout << "Simplex vector generated, size: " << v.size() << std::endl;
+
+ output_boundary_matrix(bdry_out, v, Smplx::VertexComparison());
+ output_vertex_indices(vertices_out, v);
+
+ Timer persistence_timer; persistence_timer.start();
+ ZpField zp(prime);
+ Persistence p(zp);
+ boost::progress_display show_progress(v.size());
+
+ #ifdef COUNTERS
+ Counter::CounterType max_element_count = 0;
+ unsigned max_memory = 0;
+ long max_rss = 0;
+ long max_ixrss = 0;
+ long max_idrss = 0;
+ long max_isrss = 0;
+
+ int max_uordblks = 0;
+ int max_fordblks = 0;
+ #endif
+
+ for (unsigned j = 0; j < index_in_v.size(); ++j)
+ {
+ SimplexVector::const_iterator cur = v.begin() + index_in_v[j];
+ std::vector<Index> boundary;
+ for (Smplx::BoundaryIterator bcur = cur->boundary_begin(); bcur != cur->boundary_end(); ++bcur)
+ boundary.push_back(map_of_v[*bcur]->data());
+
+ Index idx; Death d; CocyclePtr ccl;
+ bool store = cur->dimension() < skeleton;
+ boost::tie(idx, d, ccl) = p.add(boundary.begin(), boundary.end(), boost::make_tuple(cur->dimension(), size(*cur)), store, index_in_v[j]);
+
+ // c[*cur] = idx;
+ if (store)
+ map_of_v[*cur]->data() = idx;
+
+ if (d && (size(*cur) - d->get<1>()) > 0)
+ {
+ AssertMsg(d->get<0>() == cur->dimension() - 1, "Dimensions must match");
+ diagram_out << (cur->dimension() - 1) << " " << d->get<1>() << " " << size(*cur) << std::endl;
+ }
+ ++show_progress;
+
+ #ifdef COUNTERS
+ max_element_count = std::max(max_element_count, cCohomologyElementCount->count);
+ #endif
+ }
+ // output infinte persistence pairs
+ for (Persistence::CocycleIndex cur = p.begin(); cur != p.end(); ++cur)
+ diagram_out << cur->birth.get<0>() << " " << cur->birth.get<1>() << " inf" << std::endl;
+ persistence_timer.stop();
+
+
+ // p.show_cocycles();
+ // Output alive cocycles of dimension 1
+ if (!cocycle_prefix.empty())
+ {
+ unsigned i = 0;
+ for (Persistence::Cocycles::const_iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ if (cur->birth.get<0>() != 1) continue;
+ output_cocycle(cocycle_prefix, i, v, cur->birth, cur->zcolumn, prime);
+ // std::cout << "Cocycle of dimension: " << cur->birth.get<0>() << " born at " << cur->birth.get<1>() << std::endl;
+ ++i;
+ }
+ }
+ total_timer.stop();
+ rips_timer.check("Rips timer");
+ persistence_timer.check("Persistence timer");
+ total_timer.check("Total timer");
+
+ #ifdef COUNTERS
+ std::cout << "Max element count: " << max_element_count << std::endl;
+ #endif
+}
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, ZpField::Element& prime, std::string& boundary_name, std::string& cocycle_prefix, std::string& vertices_name, std::string& diagram_name)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<Dimension>(&skeleton)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("prime,p", po::value<ZpField::Element>(&prime)->default_value(11), "Prime p for the field F_p")
+ ("max-distance,m", po::value<DistanceType>(&max_distance)->default_value(Infinity), "Maximum value for the Rips complex construction")
+ ("boundary,b", po::value<std::string>(&boundary_name), "Filename where to output the boundary matrix")
+ ("cocycle,c", po::value<std::string>(&cocycle_prefix), "Prefix of the filename where to output the 1-dimensional cocycles")
+ ("vertices,v", po::value<std::string>(&vertices_name), "Filename where to output the simplex-vertex mapping")
+ ("diagram,d", po::value<std::string>(&diagram_name), "Filename where to output the persistence diagram");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/rips-pairwise-cohomology.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+from dionysus import Simplex, CohomologyPersistence, points_file, PairwiseDistances, ExplicitDistances, Rips, data_dim_cmp
+from sys import argv, exit
+import time
+
+def main(filename, skeleton, max, prime = 11):
+ points = [p for p in points_file(filename)]
+ print '#', time.asctime(), "Points read"
+ distances = PairwiseDistances(points)
+ distances = ExplicitDistances(distances) # speeds up generation of the Rips complex at the expense of memory usage
+ rips = Rips(distances)
+ print '#', time.asctime(), "Rips initialized"
+
+ simplices = []
+ rips.generate(skeleton, max, simplices.append)
+ print '#', time.asctime(), "Generated complex: %d simplices" % len(simplices)
+
+ # While this step is unnecessary (Filtration below can be passed rips.cmp),
+ # it greatly speeds up the running times
+ for s in simplices: s.data = rips.eval(s)
+ print '#', time.asctime(), simplices[0], '...', simplices[-1]
+
+ simplices.sort(data_dim_cmp)
+ print '#', time.asctime(), "Simplices sorted"
+
+ ch = CohomologyPersistence(prime)
+ complex = {}
+
+ for s in simplices:
+ i,d = ch.add([complex[sb] for sb in s.boundary], (s.dimension(), s.data), store = (s.dimension() < skeleton))
+ complex[s] = i
+ if d:
+ dimension, birth = d
+ print dimension, birth, s.data
+ # else birth
+
+ for ccl in ch:
+ dimension, birth = ccl.birth
+ if dimension >= skeleton: continue
+ print dimension, birth, 'inf' # dimension, simplex data = birth
+ print "# Cocycle at (dim=%d, birth=%f)" % ccl.birth
+ for e in ccl:
+ print "# ", e.si.order, normalized(e.coefficient, prime)
+
+def normalized(coefficient, prime):
+ if coefficient > prime/2:
+ return coefficient - prime
+ return coefficient
+
+if __name__ == '__main__':
+ if len(argv) < 4:
+ print "Usage: %s POINTS SKELETON MAX [PRIME=11]" % argv[0]
+ exit()
+
+ filename = argv[1]
+ skeleton = int(argv[2])
+ max = float(argv[3])
+ prime = (len(argv) > 4 and argv[4]) or 11
+
+ main(filename, skeleton, max, prime)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/rips-weighted-cohomology.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,185 @@
+#include <topology/cohomology-persistence.h>
+#include <topology/weighted-rips.h>
+
+#include <geometry/weighted-l2distance.h>
+#include <geometry/distances.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+#include <utilities/property-maps.h>
+#include <utilities/timer.h>
+#include <utilities/log.h>
+
+#include <string>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#include "wrappers.h"
+
+typedef PairwiseDistances<PointContainer, WeightedL2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+typedef PairDistances::IndexType Vertex;
+
+typedef boost::tuple<Dimension, DistanceType> BirthInfo;
+typedef CohomologyPersistence<BirthInfo, Wrapper<unsigned> > Persistence;
+typedef Persistence::SimplexIndex Index;
+typedef Persistence::Death Death;
+typedef Persistence::CocyclePtr CocyclePtr;
+
+typedef WeightedRips<PairDistances, Simplex<Vertex, Index> > Generator;
+typedef Generator::Simplex Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef SimplexVector::const_iterator SV_const_iterator;
+
+#include "output.h" // for output_*()
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, ZpField::Element& prime, std::string& boundary_name, std::string& cocycle_prefix, std::string& vertices_name, std::string& diagram_name);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Dimension skeleton;
+ DistanceType max_distance;
+ ZpField::Element prime;
+ std::string infilename, boundary_name, cocycle_prefix, vertices_name, diagram_name;
+
+ program_options(argc, argv, infilename, skeleton, max_distance, prime, boundary_name, cocycle_prefix, vertices_name, diagram_name);
+ std::ofstream bdry_out(boundary_name.c_str());
+ std::ofstream vertices_out(vertices_name.c_str());
+ std::ofstream diagram_out(diagram_name.c_str());
+ std::cout << "Boundary matrix: " << boundary_name << std::endl;
+ std::cout << "Cocycles: " << cocycle_prefix << "*.ccl" << std::endl;
+ std::cout << "Vertices: " << vertices_name << std::endl;
+ std::cout << "Diagram: " << diagram_name << std::endl;
+
+ Timer total_timer; total_timer.start();
+ PointContainer points;
+ read_weighted_points(infilename, points);
+
+ PairDistances distances(points);
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ Generator::Comparison cmp(distances);
+ SimplexVector v;
+
+ Timer rips_timer; rips_timer.start();
+ rips.generate(skeleton, max_distance, make_push_back_functor(v));
+
+ /* Keep simplices sorted lexicographically (so that we can binary search through them) */
+ std::sort(v.begin(), v.end(), Smplx::VertexComparison());
+
+ /* We also need the simplices sorted by value though for the filtration:
+ index_in_v[j] refers to the simplex v[index_in_v[j]] */
+ std::vector<unsigned> index_in_v(v.size());
+ for (unsigned idx = 0; idx < v.size(); ++idx)
+ index_in_v[idx] = idx;
+ std::sort(index_in_v.begin(), index_in_v.end(), IndirectIndexComparison<SimplexVector, Generator::Comparison>(v, cmp));
+
+ /* Set up map access to the lexicographically sorted simplices */
+ BinarySearchMap<Smplx, SimplexVector::iterator, Smplx::VertexComparison> map_of_v(v.begin(), v.end());
+
+ rips_timer.stop();
+ std::cout << "Simplex vector generated, size: " << v.size() << std::endl;
+
+ /*output_boundary_matrix(bdry_out, v, Smplx::VertexComparison());
+ output_vertex_indices(vertices_out, v);*/
+
+ Timer persistence_timer; persistence_timer.start();
+ ZpField zp(prime);
+ Persistence p(zp);
+ boost::progress_display show_progress(v.size());
+ for (unsigned j = 0; j < index_in_v.size(); ++j)
+ {
+ SimplexVector::const_iterator cur = v.begin() + index_in_v[j];
+ std::vector<Index> boundary;
+ for (Smplx::BoundaryIterator bcur = cur->boundary_begin(); bcur != cur->boundary_end(); ++bcur)
+ boundary.push_back(map_of_v[*bcur]->data());
+
+ Index idx; Death d; CocyclePtr ccl;
+ bool store = cur->dimension() < skeleton;
+ boost::tie(idx, d, ccl) = p.add(boundary.begin(), boundary.end(), boost::make_tuple(cur->dimension(), size(*cur)), store, index_in_v[j]);
+
+ if (store)
+ map_of_v[*cur]->data() = idx;
+
+ if (d && (size(*cur) - d->get<1>()) > 0)
+ {
+ AssertMsg(d->get<0>() == cur->dimension() - 1, "Dimensions must match");
+ diagram_out << (cur->dimension() - 1) << " " << d->get<1>() << " " << size(*cur) << std::endl;
+ }
+ ++show_progress;
+ }
+ // output infinte persistence pairs
+ for (Persistence::CocycleIndex cur = p.begin(); cur != p.end(); ++cur)
+ diagram_out << cur->birth.get<0>() << " " << cur->birth.get<1>() << " inf" << std::endl;
+ persistence_timer.stop();
+
+
+ // p.show_cocycles();
+ // Output alive cocycles of dimension 1
+ unsigned i = 0;
+ for (Persistence::Cocycles::const_iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ if (cur->birth.get<0>() != 1) continue;
+ output_cocycle(cocycle_prefix, i, v, cur->birth, cur->zcolumn, prime);
+ // std::cout << "Cocycle of dimension: " << cur->birth.get<0>() << " born at " << cur->birth.get<1>() << std::endl;
+ ++i;
+ }
+ total_timer.stop();
+ rips_timer.check("Rips timer");
+ persistence_timer.check("Persistence timer");
+ total_timer.check("Total timer");
+}
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, ZpField::Element& prime, std::string& boundary_name, std::string& cocycle_prefix, std::string& vertices_name, std::string& diagram_name)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<Dimension>(&skeleton)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("prime,p", po::value<ZpField::Element>(&prime)->default_value(11), "Prime p for the field F_p")
+ ("max-distance,m", po::value<DistanceType>(&max_distance)->default_value(Infinity), "Maximum value for the Rips complex construction")
+ ("boundary,b", po::value<std::string>(&boundary_name), "Filename where to output the boundary matrix")
+ ("cocycle,c", po::value<std::string>(&cocycle_prefix), "Prefix of the filename where to output the 1-dimensional cocycles")
+ ("vertices,v", po::value<std::string>(&vertices_name), "Filename where to output the simplex-vertex mapping")
+ ("diagram,d", po::value<std::string>(&diagram_name), "Filename where to output the persistence diagram");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/triangle-cohomology.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,94 @@
+#include <topology/simplex.h>
+#include <topology/cohomology-persistence.h>
+
+#include <utilities/log.h>
+#include <utilities/indirect.h>
+
+#include <vector>
+#include <map>
+#include <iostream>
+
+#include <boost/tuple/tuple.hpp>
+
+#if 1
+#include <fstream>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/serialization/vector.hpp>
+#endif
+
+typedef CohomologyPersistence<unsigned> Persistence;
+typedef Persistence::SimplexIndex Index;
+typedef Persistence::Death Death;
+typedef Persistence::CocyclePtr CocyclePtr;
+
+typedef unsigned Vertex;
+typedef Simplex<Vertex, double> Smplx;
+
+typedef std::map<Smplx, Index,
+ Smplx::VertexComparison> Complex;
+typedef std::vector<Smplx> SimplexVector;
+
+
+void fillTriangleSimplices(SimplexVector& c)
+{
+ typedef std::vector<Vertex> VertexVector;
+ VertexVector vertices(4);
+ vertices[0] = 0; vertices[1] = 1; vertices[2] = 2;
+ vertices[3] = 0;
+
+ VertexVector::const_iterator bg = vertices.begin();
+ VertexVector::const_iterator end = vertices.end();
+ c.push_back(Smplx(bg, bg + 1, 0)); // 0 = A
+ c.push_back(Smplx(bg + 1, bg + 2, 1)); // 1 = B
+ c.push_back(Smplx(bg + 2, bg + 3, 2)); // 2 = C
+ c.push_back(Smplx(bg, bg + 2, 2.5)); // AB
+ c.push_back(Smplx(bg + 1, bg + 3, 2.9)); // BC
+ c.push_back(Smplx(bg + 2, end, 3.5)); // CA
+ c.push_back(Smplx(bg, bg + 3, 5)); // ABC
+}
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo(RLOG_CHANNEL("info"));
+ stderrLog.subscribeTo(RLOG_CHANNEL("error"));
+ stderrLog.subscribeTo(RLOG_CHANNEL("topology"));
+#endif
+
+ SimplexVector v;
+ fillTriangleSimplices(v);
+ std::sort(v.begin(), v.end(), Smplx::DataComparison());
+ std::cout << "Simplices filled" << std::endl;
+ for (SimplexVector::const_iterator cur = v.begin(); cur != v.end(); ++cur)
+ std::cout << " " << *cur << std::endl;
+
+ // Compute persistence
+ Complex c;
+ ZpField zp(11);
+ Persistence p(zp);
+ unsigned i = 0;
+ for (SimplexVector::const_iterator cur = v.begin(); cur != v.end(); ++cur)
+ {
+ std::cout << "-------" << std::endl;
+
+ std::vector<Index> boundary;
+ for (Smplx::BoundaryIterator bcur = cur->boundary_begin();
+ bcur != cur->boundary_end(); ++bcur)
+ boundary.push_back(c[*bcur]);
+
+ Index idx; Death d; CocyclePtr ccl;
+ boost::tie(idx, d, ccl) = p.add(boundary.begin(), boundary.end(), i++);
+ c[*cur] = idx;
+ if (d)
+ std::cout << (cur->dimension() - 1) << " " << *d << " " << (i-1) << std::endl;
+ // the dimension above is adjusted for what it would look like in homology
+ // (i.e. when a 1 class kills 0, it's really that in cohomology forward 0 kills 1,
+ // in cohomology backward 1 kills 0, and in homology 1 kills 0)
+
+ p.show_cocycles();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/cohomology/wrappers.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,59 @@
+/**
+ * Wrapper class
+ *
+ * At points we need to wrap primitive data types in classes, so that we can
+ * pass them as template arguments to classes that like to inherit from their
+ * template arguments--this is done in CohomologyPersistence, for example.
+ */
+
+template<typename Primitive>
+class Wrapper
+{
+
+ public:
+
+ Wrapper () {}
+ Wrapper (Primitive v) { value = v; }
+
+ void setValue (const Primitive &v) { value = v; }
+ Primitive &getValue () { return value; }
+
+ /* provide seemless integration */
+ Wrapper &operator =(const Primitive &v) { setValue(v); return *this; }
+ operator Primitive() { return getValue; }
+
+ protected:
+
+ Primitive value;
+
+};
+
+/**
+ * IndirectIndexComparison class
+ *
+ * This class serves as a comparison function for arrays that are being sorted
+ * even though they only contain *indices* to the real data. Therefore, a reference
+ * to the original data as well as the data comparison function needs to be passed
+ * to this class for it to be functional.
+ */
+
+template<class DataContainer, class DataComparison>
+class IndirectIndexComparison
+{
+
+ public:
+
+ IndirectIndexComparison(const DataContainer &dstor, const DataComparison &dcmp) :
+ container(dstor), comparison(dcmp) { }
+
+ bool operator()(const unsigned &idx_1, const unsigned &idx_2) const
+ {
+ return comparison(container[idx_1], container[idx_2]);
+ }
+
+ private:
+
+ const DataContainer &container;
+ const DataComparison &comparison;
+
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/consistency/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+set (targets
+ rips-consistency-zigzag)
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/consistency/make-zigzag-subsamples.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+# Creates POINTS and SUBSAMPLES files from a list of points file in a format
+# suitable for rips-consistency-zigzag.
+
+
+from sys import argv, exit
+
+
+def create_subsamples(points_fn, subsamples_fn, points_list):
+ points = []
+ count = []
+ for pfn in points_list:
+ count.append(0)
+ with open(pfn) as f:
+ for line in f:
+ if line.startswith('#'): continue
+ points.append(line)
+ count[-1] += 1
+
+ with open(points_fn, 'w') as f:
+ for line in points:
+ f.write(line)
+
+ cur = 0
+ counts = []
+ for c in count:
+ counts.append(' '.join(map(str, xrange(cur, cur+c))) + '\n')
+ cur += c
+ # counts.append(' '.join(map(str, xrange(cur-c, cur+c))) + '\n')
+
+ with open(subsamples_fn, 'w') as f:
+ f.writelines(counts)
+
+
+if __name__ == '__main__':
+ if len(argv) < 4:
+ print "Usage: %s POINTS SUBSAMPLES POINTS1 [POINTS2 [POINTS3 [...]]]" % argv[0]
+ print
+ print "Creates a file POINTS with the union of POINTS* and SUBSAMPLES which lists"
+ print "the indices of the points one per line"
+ exit()
+
+ points_fn = argv[1]
+ subsamples_fn = argv[2]
+ points_list = argv[3:]
+ create_subsamples(points_fn, subsamples_fn, points_list)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/consistency/rips-consistency-zigzag.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,343 @@
+#include <topology/rips.h>
+#include <topology/zigzag-persistence.h>
+
+#include <geometry/l2distance.h> // Point, PointContainer, L2DistanceType, read_points
+#include <geometry/distances.h>
+
+#include <utilities/types.h>
+#include <utilities/containers.h>
+#include <utilities/log.h>
+#include <utilities/memory.h> // for report_memory()
+#include <utilities/timer.h>
+
+#include <map>
+#include <cmath>
+#include <fstream>
+#include <sstream>
+#include <stack>
+#include <cstdlib>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#ifdef COUNTERS
+static Counter* cComplexSize = GetCounter("rips/size");
+static Counter* cOperations = GetCounter("rips/operations");
+#endif // COUNTERS
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+
+typedef PairDistances::IndexType Vertex;
+typedef std::vector<Vertex> SubsampleVector;
+
+typedef Simplex<Vertex> Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef std::set<Smplx, Smplx::VertexDimensionComparison> SimplexSet;
+
+typedef std::vector<Vertex> VertexVector;
+typedef std::vector<DistanceType> EpsilonVector;
+typedef std::vector<std::pair<Vertex, Vertex> > EdgeVector;
+
+typedef Rips<PairDistances, Smplx> RipsGenerator;
+typedef RipsGenerator::Evaluator SimplexEvaluator;
+
+struct BirthInfo;
+typedef ZigzagPersistence<BirthInfo> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef std::map<Smplx, Index,
+ Smplx::VertexDimensionComparison> Complex;
+typedef Zigzag::ZColumn Boundary;
+
+// Information we need to know when a class dies
+struct BirthInfo
+{
+ BirthInfo(Dimension d = 0, unsigned i = 0, bool u = false):
+ dimension(d), index(i), un(u) {}
+
+ bool operator<(const BirthInfo& other) const { if (index == other.index) return (!un && other.un); else return index < other.index; }
+ bool operator>(const BirthInfo& other) const { return other.operator<(*this); }
+ bool operator>=(const BirthInfo& other) const { return !operator<(other); }
+ bool operator<=(const BirthInfo& other) const { return !operator>(other); }
+
+ Dimension dimension;
+ unsigned index;
+ bool un;
+};
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi)
+{ out << "(d=" << bi.dimension << ") " << bi.index; if (bi.un) out << " U " << (bi.index + 1); return out; }
+
+// Forward declarations of auxilliary functions
+void report_death(std::ostream& out, const BirthInfo& birth, const BirthInfo& death, unsigned skeleton_dimension);
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b);
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& epsilon,
+ std::string& points_filename,
+ std::string& subsample_filename,
+ std::string& outfilename);
+void add_simplex(const Smplx& s, const BirthInfo& birth, const BirthInfo& death,
+ Complex& complex, Zigzag& zz, std::ostream& out, Timer& add, unsigned skeleton_dimension);
+void remove_simplex(const Smplx& s, const BirthInfo& birth, const BirthInfo& death,
+ Complex& complex, Zigzag& zz, std::ostream& out, Timer& remove, unsigned skeleton_dimension);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Timer total, remove, add, coface, generate;
+ total.start();
+
+#if 0
+ SetFrequency(cOperations, 25000);
+ SetTrigger(cOperations, cComplexSize);
+#endif
+
+ unsigned skeleton_dimension;
+ float epsilon;
+ std::string points_filename, subsample_filename, outfilename;
+ process_command_line_options(argc, argv, skeleton_dimension, epsilon,
+ points_filename, subsample_filename, outfilename);
+
+ // Read in points
+ PointContainer points;
+ read_points(points_filename, points);
+
+ // Read in subsamples
+ std::ifstream subsample_in(subsample_filename.c_str());
+ std::vector<SubsampleVector> subsamples;
+ subsamples.push_back(SubsampleVector()); // Empty subsample to start from
+ while(subsample_in)
+ {
+ std::string line; std::getline(subsample_in, line);
+ std::istringstream line_in(line);
+ subsamples.push_back(SubsampleVector());
+ unsigned sample;
+ while (line_in >> sample)
+ subsamples.back().push_back(sample);
+ }
+ AssertMsg(subsamples.front().size() == 0, "The first subsample should be empty");
+ AssertMsg(subsamples.back().size() == 0, "The last subsample should be empty"); // it's a convenient artifact of the stream processing above
+
+ std::cout << "Subsample size:" << std::endl;
+ for (unsigned i = 0; i < subsamples.size(); ++i)
+ std::cout << " " << subsamples[i].size() << std::endl;
+
+ // Create output file
+ std::ofstream out(outfilename.c_str());
+
+ // Create pairwise distances
+ PairDistances distances(points);
+
+
+ // Construct zigzag
+ Complex complex;
+ Zigzag zz;
+ RipsGenerator rips(distances);
+ SimplexEvaluator size(distances);
+ SimplexVector subcomplex, across;
+
+ rInfo("Commencing computation");
+ boost::progress_display show_progress(subsamples.size() - 1);
+ for (unsigned i = 0; i < subsamples.size() - 1; ++i)
+ {
+ // Take union of subsamples i and i+1
+ SubsampleVector forward_difference, backward_difference;
+ std::set_difference(subsamples[i+1].begin(), subsamples[i+1].end(),
+ subsamples[i].begin(), subsamples[i].end(),
+ std::back_inserter(forward_difference));
+ std::set_difference(subsamples[i].begin(), subsamples[i].end(),
+ subsamples[i+1].begin(), subsamples[i+1].end(),
+ std::back_inserter(backward_difference));
+ rInfo("Forward difference size: %d", forward_difference.size());
+ rInfo("Backward difference size: %d", backward_difference.size());
+
+#if LOGGING
+ rDebug("Forward difference:");
+ for (SubsampleVector::const_iterator cur = forward_difference.begin(); cur != forward_difference.end(); ++cur)
+ rDebug(" %d", *cur);
+
+ rDebug("Backward difference:");
+ for (SubsampleVector::const_iterator cur = backward_difference.begin(); cur != backward_difference.end(); ++cur)
+ rDebug(" %d", *cur);
+#endif
+
+ // Add simplices: take star of the vertices in subsamples[i+1] - subsamples[i]
+ // by first computing the Rips complex on those vertices, and then
+ // taking the star of each individual simplex (perhaps not the most
+ // efficient procedure, but it will do for the first pass)
+ rInfo("Adding");
+ subcomplex.clear(); across.clear();
+ generate.start();
+ rips.generate(skeleton_dimension, epsilon, make_push_back_functor(subcomplex), forward_difference.begin(), forward_difference.end());
+ std::sort(subcomplex.begin(), subcomplex.end(), Smplx::VertexDimensionComparison());
+ generate.stop();
+ rInfo(" Subcomplex size: %d", subcomplex.size());
+ for (SimplexVector::const_iterator cur = subcomplex.begin(); cur != subcomplex.end(); ++cur)
+ {
+ add_simplex(*cur, BirthInfo(cur->dimension(), i, true), BirthInfo(cur->dimension() - 1, i),
+ complex, zz, out, add, skeleton_dimension);
+
+ // Record cofaces with all other vertices outside of the subcomplex
+ coface.start();
+ rips.cofaces(*cur, skeleton_dimension, epsilon, make_push_back_functor(across), subsamples[i].begin(), subsamples[i].end());
+ coface.stop();
+ }
+ std::sort(across.begin(), across.end(), Smplx::VertexDimensionComparison());
+ rInfo(" Subcomplex simplices added");
+ rInfo(" Cross simplices size: %d", across.size());
+
+ // Add simplices that cut across VR(K_i) and VR(K_{i+1})
+ for (SimplexVector::const_iterator cur = across.begin(); cur != across.end(); ++cur)
+ add_simplex(*cur, BirthInfo(cur->dimension(), i, true), BirthInfo(cur->dimension() - 1, i),
+ complex, zz, out, add, skeleton_dimension);
+ rInfo(" Cross simplices added");
+
+
+ // Remove simplices: take star of the vertices in subsamples[i] - subsamples[i+1]
+ rInfo("Removing");
+ subcomplex.clear(); across.clear();
+ generate.start();
+ rips.generate(skeleton_dimension, epsilon, make_push_back_functor(subcomplex), backward_difference.begin(), backward_difference.end());
+ std::sort(subcomplex.begin(), subcomplex.end(), Smplx::VertexDimensionComparison());
+ generate.stop();
+ rInfo(" Subcomplex size: %d", subcomplex.size());
+
+ for (SimplexVector::const_iterator cur = subcomplex.begin(); cur != subcomplex.end(); ++cur)
+ rips.cofaces(*cur, skeleton_dimension, epsilon, make_push_back_functor(across), subsamples[i+1].begin(), subsamples[i+1].end());
+ std::sort(across.begin(), across.end(), Smplx::VertexDimensionComparison());
+ rInfo(" Cross simplices size: %d", across.size());
+
+ for (SimplexVector::const_reverse_iterator cur = across.rbegin(); cur != (SimplexVector::const_reverse_iterator)across.rend(); ++cur)
+ remove_simplex(*cur, BirthInfo(cur->dimension() - 1, i+1), BirthInfo(cur->dimension(), i, true),
+ complex, zz, out, remove, skeleton_dimension);
+ rInfo(" Cross simplices removed");
+
+ for (SimplexVector::const_reverse_iterator cur = subcomplex.rbegin(); cur != (SimplexVector::const_reverse_iterator)subcomplex.rend(); ++cur)
+ remove_simplex(*cur, BirthInfo(cur->dimension() - 1, i+1), BirthInfo(cur->dimension(), i, true),
+ complex, zz, out, remove, skeleton_dimension);
+ rInfo(" Subcomplex simplices removed");
+
+ Dimension betti_1 = 0;
+ for (Zigzag::ZIndex cur = zz.begin(); cur != zz.end(); ++cur)
+ if (cur->birth.dimension == 1 && zz.is_alive(cur)) ++betti_1;
+
+ rInfo("Complex size: %d, Betti_1 = %d", complex.size(), betti_1);
+
+ ++show_progress;
+ }
+
+ std::cout << std::endl;
+ total.stop();
+ remove.check("Remove timer ");
+ add.check ("Add timer ");
+ coface.check("Coface timer ");
+ total.check ("Total timer ");
+}
+
+
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b)
+{
+ // rDebug(" Boundary of <%s>", tostring(s).c_str());
+ for (Smplx::BoundaryIterator cur = s.boundary_begin(); cur != s.boundary_end(); ++cur)
+ {
+ b.append(c[*cur], zz.cmp);
+ // rDebug(" %d", c[*cur]->order);
+ }
+}
+
+void report_death(std::ostream& out, const BirthInfo& birth, const BirthInfo& death, unsigned skeleton_dimension)
+{
+ if (birth.dimension < skeleton_dimension && death >= birth)
+ out << birth << " --- " << death << std::endl;
+}
+
+void add_simplex(const Smplx& s, const BirthInfo& birth, const BirthInfo& death,
+ Complex& complex, Zigzag& zz, std::ostream& out, Timer& add, unsigned skeleton_dimension)
+{
+ rDebug("Adding simplex: %s", tostring(s).c_str());
+
+ Index idx; Death d; Boundary b;
+ make_boundary(s, complex, zz, b);
+ add.start();
+ boost::tie(idx, d) = zz.add(b, birth);
+ if (d) report_death(out, *d, death, skeleton_dimension);
+ add.stop();
+ complex.insert(std::make_pair(s, idx));
+}
+
+void remove_simplex(const Smplx& s, const BirthInfo& birth, const BirthInfo& death,
+ Complex& complex, Zigzag& zz, std::ostream& out, Timer& remove, unsigned skeleton_dimension)
+{
+ rDebug("Removing simplex: %s", tostring(s).c_str());
+
+ Complex::iterator si = complex.find(s);
+ remove.start();
+ Death d = zz.remove(si->second, birth);
+ remove.stop();
+ if (d) report_death(out, *d, death, skeleton_dimension);
+ complex.erase(si);
+}
+
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& epsilon,
+ std::string& points_filename,
+ std::string& subsample_filename,
+ std::string& outfilename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("points-file", po::value<std::string>(&points_filename), "Point set whose Rips consistency zigzag we want to compute")
+ ("subsample-file", po::value<std::string>(&subsample_filename),"Subsample list")
+ ("output-file", po::value<std::string>(&outfilename), "Location to save persistence pairs");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<unsigned>(&skeleton_dimension)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("epsilon,e", po::value<float>(&epsilon)->default_value(0), "epsilon to use when computing the Rips complex");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("points-file", 1);
+ pos.add("subsample-file", 1);
+ pos.add("output-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("points-file") || !vm.count("subsample-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] points-file subsample-file output-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/filtration/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+set (targets
+ filtration-homology)
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/filtration/filtration-homology.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,140 @@
+#include <topology/simplex.h>
+#include <topology/filtration.h>
+#include <topology/dynamic-persistence.h>
+#include <topology/persistence-diagram.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+#include <utilities/timer.h>
+
+#include <vector>
+#include <fstream>
+
+#include <boost/program_options.hpp>
+
+typedef unsigned Vertex;
+typedef Simplex<Vertex> Smplx;
+typedef Filtration<Smplx> Fltr;
+typedef DynamicPersistenceChains<> Persistence;
+typedef Persistence::Chain Chain;
+typedef PersistenceDiagram<> PDgm;
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, std::string& diagram_name, Dimension& cycle_dimension, std::string& cycle_filename);
+void read_filtration(const std::string& filename, Fltr& filtration);
+void output_chain(std::ostream& out, const Chain& chain, const Persistence& persistence);
+
+int main(int argc, char* argv[])
+{
+ Dimension skeleton, cycle_dimension;
+ std::string infilename, diagram_name, cycle_filename;
+
+ program_options(argc, argv, infilename, skeleton, diagram_name, cycle_dimension, cycle_filename);
+ std::ofstream diagram_out(diagram_name.c_str());
+ std::cout << "Diagram: " << diagram_name << std::endl;
+ std::ofstream cycle_out(cycle_filename.c_str());
+ std::cout << "Cycles: " << cycle_filename << std::endl;
+
+ Fltr f;
+ read_filtration(infilename, f);
+ std::cout << "# Read filtration of size: " << f.size() << std::endl;
+
+ Timer persistence_timer; persistence_timer.start();
+ Persistence p(f);
+ p.pair_simplices();
+ persistence_timer.stop();
+
+#if 1
+ // Output chains and pairs
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(f);
+ for (Persistence::iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ if (!cur->sign()) // only negative simplices have non-empty cycles
+ {
+ Persistence::OrderIndex birth = cur->pair; // the cycle that cur killed was born when we added birth (another simplex)
+
+ const Smplx& b = m[birth];
+ const Smplx& d = m[cur];
+
+ if (b.dimension() >= skeleton) continue;
+ diagram_out << b.dimension() << " " << (p.iterator_to(cur->pair) - p.begin()) << " " << (cur - p.begin()) << std::endl;
+ } else if (cur->unpaired()) // positive could be unpaired
+ {
+ const Smplx& b = m[cur];
+
+ if (b.dimension() >= skeleton) continue;
+ diagram_out << b.dimension() << " " << (cur - p.begin()) << " inf" << std::endl;
+
+ if (b.dimension() != cycle_dimension) continue;
+ output_chain(cycle_out, cur->chain, p);
+ }
+ }
+#endif
+
+ persistence_timer.check("# Persistence timer");
+}
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, std::string& diagram_name, Dimension& cycle_dimension, std::string& cycle_filename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<Dimension>(&skeleton)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("diagram,d", po::value<std::string>(&diagram_name), "Filename where to output the persistence diagram")
+ ("cycle-dimension,c", po::value<Dimension>(&cycle_dimension)->default_value(1), "Dimension of the essential cycles to output")
+ ("cycle-filename,o", po::value<std::string>(&cycle_filename), "Filename where to output the essential cycles");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
+
+void read_filtration(const std::string& filename, Fltr& filtration)
+{
+ std::ifstream in(filename.c_str());
+ std::string line;
+ while(std::getline(in, line))
+ {
+ if (line[0] == '#') continue; // comment line in the file
+ std::stringstream linestream(line);
+ Vertex v;
+ Smplx s;
+ while (linestream >> v)
+ s.add(v);
+ filtration.push_back(s);
+ }
+}
+
+void output_chain(std::ostream& out, const Chain& chain, const Persistence& persistence)
+{
+ out << "---\n";
+ for (Chain::const_iterator iter = chain.begin(); iter != chain.end(); ++iter)
+ out << persistence.iterator_to(*iter) - persistence.begin() << '\n';
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/fitness/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,10 @@
+set (targets
+ avida-distance
+ avida-rips-distance
+ #avida-landscape
+ )
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/fitness/avida-distance.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,93 @@
+#include <iostream>
+#include <vector>
+#include <algorithm>
+#include "avida-population-detail.h"
+
+#include <topology/filtration.h>
+#include <topology/simplex.h>
+#include <topology/static-persistence.h>
+
+
+typedef Simplex<AvidaOrganismDetail::IDType, double> Smplx;
+typedef std::vector<Smplx> Complex;
+typedef Filtration<Smplx> Fltr;
+typedef StaticPersistence<> Persistence;
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+ //stdoutLog.subscribeTo(RLOG_CHANNEL("info"));
+#endif
+
+ if (argc < 2)
+ {
+ std::cout << "USAGE: avida FILENAME" << std::endl;
+ return 0;
+ }
+
+ AvidaPopulationDetail population(argv[1]);
+ const AvidaPopulationDetail::OrganismVector& organisms = population.get_organisms();
+
+ rInfo("Number of organisms: %d", organisms.size());
+ for (int i = 0; i < population.get_organisms().size(); ++i)
+ rInfo("%d (%s) %f %d %d", organisms[i].id(),
+ organisms[i].genome().c_str(),
+ organisms[i].fitness(),
+ organisms[i].length(),
+ organisms[i].genome().size());
+
+ // Distance function filtration
+ Complex simplices;
+
+ // Insert edges between all the organisms
+ AvidaOrganismDetail::DistanceType avg_distance = 0;
+ for (AvidaOrganismDetail::CountType i = 0; i < organisms.size(); ++i)
+ {
+ simplices.push_back(0); // all vertices have 0 value
+ simplices.back().add(organisms[i].id());
+
+ for (AvidaOrganismDetail::CountType j = i+1; j < organisms.size(); ++j)
+ {
+ avg_distance += organisms[i].genome_distance(organisms[j]);
+ simplices.push_back(Smplx(organisms[i].genome_distance(organisms[j])));
+ simplices.back().add(organisms[i].id());
+ simplices.back().add(organisms[j].id());
+ }
+ }
+ rInfo("Average distance: %f", float(avg_distance)/
+ ((organisms.size()*organisms.size() - organisms.size())/2));
+
+ Fltr f(simplices.begin(), simplices.end(), DataDimensionComparison<Smplx>());
+ Persistence p(f);
+ p.pair_simplices();
+
+ std::cout << "Outputting histogram of death values" << std::endl;
+ typedef std::vector<RealType> DeathVector;
+ DeathVector deaths;
+ Smplx::DataEvaluator eval;
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(f);
+ for (Persistence::iterator i = p.begin(); i != p.end(); ++i)
+ {
+ if (i->unpaired()) continue;
+ if (i->sign())
+ {
+ const Smplx& s = m[i];
+ const Smplx& t = m[i->pair];
+ AssertMsg(s.dimension() == 0, "Expecting only 0-dimensional diagram");
+ AssertMsg(eval(s) == 0, "Expecting only 0 birth values in 0-D diagram ");
+ deaths.push_back(eval(t));
+ }
+ }
+
+ // Produce histogram
+ std::sort(deaths.begin(), deaths.end());
+ for (DeathVector::iterator cur = deaths.begin(); cur != deaths.end(); )
+ {
+ DeathVector::iterator nw = std::find_if(cur, deaths.end(),
+ std::bind2nd(std::greater<RealType>(), *cur));
+ std::cout << *cur << "\t" << (nw - cur) << std::endl;
+ cur = nw;
+ }
+ std::cout << "Total: " << deaths.size() + 1; // +1 for the unpaired
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/fitness/avida-landscape.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,209 @@
+#include <iostream>
+#include <vector>
+#include <algorithm>
+#include <string>
+#include "avida-population-detail.h"
+
+#include <boost/program_options.hpp>
+
+#include <topology/lowerstarfiltration.h>
+
+
+// Lower-star filtration
+typedef AvidaPopulationDetail::OrganismIndex OrganismIndex;
+struct OrganismVertexType;
+typedef std::vector<OrganismVertexType> VertexVector;
+typedef VertexVector::iterator VertexIndex;
+typedef LowerStarFiltration<VertexIndex> LSFiltration;
+typedef LSFiltration::Simplex Simplex;
+
+struct OrganismVertexType: public LSFiltration::VertexType<OrganismIndex>
+{
+ typedef LSFiltration::VertexType<OrganismIndex> Parent;
+ OrganismVertexType(OrganismIndex i): Parent(i) {}
+};
+
+struct OrganismVertexComparison
+{
+ public:
+ bool operator()(VertexIndex i, VertexIndex j) const
+ { return i->index()->fitness() > j->index()->fitness(); }
+ // > because of -fitness, so that maxima turn into minima
+};
+
+typedef LSFiltration::Vineyard LSVineyard;
+class StaticEvaluator: public LSVineyard::Evaluator
+{
+ public:
+ StaticEvaluator(float max_fitness):
+ max_fitness_(max_fitness) {}
+
+ virtual RealType
+ value(const Simplex& s) const
+ { return s.get_attachment()->index()->fitness()/max_fitness_; }
+
+ private:
+ float max_fitness_;
+};
+
+std::ostream& operator<<(std::ostream& out, VertexIndex i)
+{ return (out << (i->index())); }
+
+
+// Distance filtration
+typedef SimplexWithValue<VertexIndex> DistanceSimplex;
+typedef std::vector<DistanceSimplex> DistanceSimplexVector;
+typedef Filtration<DistanceSimplex> DistanceSimplexFiltration;
+
+
+namespace po = boost::program_options;
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+ stderrLog.subscribeTo(RLOG_CHANNEL("error"));
+ //stdoutLog.subscribeTo(RLOG_CHANNEL("info"));
+#endif
+
+ typedef AvidaOrganismDetail::DistanceType DistanceType;
+
+ DistanceType connected_distance;
+ bool connect_mst = false;
+ std::string population_input_fn;
+
+ // Parse program options
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&population_input_fn),
+ "Avida population file");
+
+ po::options_description visible("Allowed options");
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("distance,d", po::value<DistanceType>(&connected_distance)->default_value(0),
+ "set connected distance")
+ ("mst,m", "connect minimum spanning tree");
+ po::positional_options_description p;
+ p.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+ if (vm.count("mst")) { connect_mst = true; }
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] POPULATION" << std::endl;
+ std::cout << visible << std::endl;
+ return 1;
+ }
+
+ // Read organisms
+ AvidaPopulationDetail population(population_input_fn);
+ const AvidaPopulationDetail::OrganismVector& organisms = population.get_organisms();
+
+ rInfo("Number of organisms: %d", organisms.size());
+ float max_fitness = organisms[0].fitness();
+ for (int i = 0; i < population.get_organisms().size(); ++i)
+ {
+ max_fitness = std::max(max_fitness, organisms[i].fitness());
+ rInfo("%d (%s) %f %d %d", organisms[i].id(),
+ organisms[i].genome().c_str(),
+ organisms[i].fitness(),
+ organisms[i].length(),
+ organisms[i].genome().size());
+ }
+
+ // Order vertices
+ StaticEvaluator evaluator(max_fitness);
+ LSVineyard vineyard(&evaluator);
+ VertexVector vertices;
+ for (OrganismIndex cur = organisms.begin(); cur != organisms.end(); ++cur) vertices.push_back(cur);
+ LSFiltration fitness_filtration(vertices.begin(), vertices.end(), OrganismVertexComparison(), &vineyard);
+
+ // Compute MST and insert its edges if asked
+ if (connect_mst)
+ {
+ DistanceSimplexFiltration filtration;
+ { // Scope so that simplices is deleted once it's not needed
+ // Distance function filtration
+ DistanceSimplexVector simplices;
+
+ // Insert edges
+ for (VertexIndex i = vertices.begin(); i != vertices.end(); ++i)
+ {
+ simplices.push_back(DistanceSimplex());
+ simplices.back().add(i);
+
+ for (VertexIndex j = boost::next(i); j != vertices.end(); ++j)
+ {
+ simplices.push_back(DistanceSimplex(i->index()->genome_distance(*(j->index()))));
+ simplices.back().add(i);
+ simplices.back().add(j);
+ }
+ }
+ std::sort(simplices.begin(), simplices.end(), DimensionValueComparison<DistanceSimplex>());
+
+ for (DistanceSimplexVector::const_iterator cur = simplices.begin();
+ cur != simplices.end(); ++cur)
+ filtration.append(*cur);
+ }
+
+ filtration.fill_simplex_index_map();
+ filtration.pair_simplices(false); // pair simplices without storing trails
+
+ for (DistanceSimplexFiltration::Index i = filtration.begin(); i != filtration.end(); ++i)
+ {
+ if (i->is_paired() && !i->sign())
+ {
+ Simplex s(*i);
+ if (i->get_value() > connected_distance) // <= will be connected below
+ fitness_filtration.append(s);
+ }
+ }
+ }
+
+ // Add simplices
+ for (VertexIndex cur = vertices.begin(); cur != vertices.end(); ++cur)
+ for (VertexIndex link = boost::next(cur); link != vertices.end(); ++link)
+ if (cur->index()->genome_distance(*(link->index())) <= connected_distance)
+ {
+ Simplex s(2, cur); s.add(link);
+ fitness_filtration.append(s);
+ }
+ rInfo("Number of simplices: %d", fitness_filtration.size());
+
+ // Pair simplices
+ fitness_filtration.fill_simplex_index_map();
+ fitness_filtration.pair_simplices(false); // pair simplices without storing trails
+
+ //std::cout << "Outputting persistence pairs" << std::endl;
+ for (LSFiltration::Index i = fitness_filtration.begin(); i != fitness_filtration.end(); ++i)
+ {
+ if (i->is_paired())
+ {
+ if (i->sign())
+ {
+ AssertMsg(i->dimension() == 0, "Expecting only 0-dimensional diagram");
+ if (i->pair()->get_attachment() == i->vertices()[0]) continue; // skip non-critical pairs
+ std::cout << i->dimension() << " "
+ << evaluator.value(*i) << " "
+ << evaluator.value(*(i->pair())) << std::endl;
+ }
+ }
+ else
+ {
+ if (i->dimension() != 0) continue;
+ std::cout << i->dimension() << " "
+ << evaluator.value(*i) << " "
+ << 0 << std::endl;
+ // The infinite pair does not make sense since we are interested in
+ // max of fitness, rather than min of -fitness. However min value for
+ // fitness is 0, so it's a natural choice of the answer.
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/fitness/avida-population-detail.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,81 @@
+/**
+ * Author: Dmitriy Morozov
+ * Department of Computer Science, Duke University, 2007
+ */
+
+#ifndef __AVIDA_POPULATION_DETAIL_H__
+#define __AVIDA_POPULATION_DETAIL_H__
+
+#include <string>
+#include <vector>
+
+/**
+ * Stores organism details stored in a single line of a population detail file in data/
+ * directory of Avida's output.
+ */
+class AvidaOrganismDetail
+{
+ public:
+ typedef int IDType;
+ /// Distance between two genomes
+ typedef unsigned int DistanceType;
+ typedef unsigned int CountType;
+
+ AvidaOrganismDetail(std::string line);
+
+ DistanceType genome_distance(const AvidaOrganismDetail& other) const;
+
+ IDType id() const { return id_; }
+ float fitness() const { return fitness_; }
+ CountType length() const { return genome_length_; }
+ std::string genome() const { return genome_; }
+
+ private:
+ IDType id_, parent_id_;
+ int parent_distance_;
+ CountType num_organisms_alive_, num_organisms_ever_;
+ CountType genome_length_;
+ float merit_, gestation_time_;
+ float fitness_;
+ int update_born_, update_deactivated_, depth_phylogenetic_tree_;
+ std::string genome_;
+};
+
+/**
+ * Stores entire population details (all organisms in a given time step), i.e., stores
+ * an entire population detail file in data/ directory of Avida's ouptut.
+ */
+class AvidaPopulationDetail
+{
+ public:
+ typedef std::vector<AvidaOrganismDetail> OrganismVector;
+ typedef OrganismVector::const_iterator OrganismIndex;
+
+ AvidaPopulationDetail(std::string filename);
+
+ const OrganismVector&
+ get_organisms() const { return organisms_; }
+
+ /// \name Rips
+ /// @{
+ typedef int IndexType;
+ typedef double DistanceType;
+
+ DistanceType operator()(IndexType a, IndexType b) const { return organisms_[a].genome_distance(organisms_[b]); }
+
+ size_t size() const { return organisms_.size(); }
+ IndexType begin() const { return 0; }
+ IndexType end() const { return size(); }
+ /// @}
+
+ private:
+ OrganismVector organisms_;
+};
+
+std::ostream& operator<<(std::ostream& out, AvidaPopulationDetail::OrganismIndex i)
+{ return (out << (i->id())); }
+
+
+#include "avida-population-detail.hpp"
+
+#endif //__AVIDA_POPULATION_DETAIL_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/fitness/avida-population-detail.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,57 @@
+#include <fstream>
+#include <sstream>
+#include "utilities/log.h"
+
+/** AvidaOrganismDetail */
+
+AvidaOrganismDetail::
+AvidaOrganismDetail(std::string line)
+{
+ std::stringstream linestream(line);
+ linestream >> id_ >> parent_id_ >> parent_distance_;
+ linestream >> num_organisms_alive_ >> num_organisms_ever_;
+ linestream >> genome_length_;
+ linestream >> merit_ >> gestation_time_;
+ linestream >> fitness_;
+ linestream >> update_born_ >> update_deactivated_ >> depth_phylogenetic_tree_;
+ linestream >> genome_;
+
+ AssertMsg(genome_length_ == genome_.size(), "Genome must be of given length");
+}
+
+AvidaOrganismDetail::DistanceType
+AvidaOrganismDetail::
+genome_distance(const AvidaOrganismDetail& other) const
+{
+ AssertMsg(genome_.size() == other.genome_.size(), "Currently genome sizes must be the same for distance computation");
+ AssertMsg(genome_length_ == genome_.size(), "Genome length value must match the length of the genome string");
+ AssertMsg(other.genome_length_ == other.genome_.size(), "Genome length value must match the length of the genome string");
+
+ CountType count = 0;
+ for (CountType i = 0; i < genome_.size(); ++i)
+ if (genome_[i] != other.genome_[i])
+ ++count;
+
+ return count;
+}
+
+
+/** AvidaPopulationDetail */
+AvidaPopulationDetail::
+AvidaPopulationDetail(std::string filename)
+{
+ std::ifstream infile(filename.c_str());
+ while(infile)
+ {
+ std::string line;
+ std::getline(infile, line);
+
+ // Skip comments and empty lines
+ char c = '#';
+ std::istringstream linestream(line);
+ linestream >> c;
+ if (c == '#') continue;
+
+ organisms_.push_back(AvidaOrganismDetail(line));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/fitness/avida-rips-distance.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,89 @@
+#include <iostream>
+#include <vector>
+#include <algorithm>
+#include "avida-population-detail.h"
+
+#include <topology/filtration.h>
+#include <topology/rips.h>
+#include <topology/static-persistence.h>
+
+#include <geometry/distances.h> // for ExplicitDistances
+
+
+typedef ExplicitDistances<AvidaPopulationDetail> ExplicitDist;
+typedef Rips<ExplicitDist> RipsGen;
+typedef RipsGen::Simplex Smplx;
+
+typedef Filtration<Smplx> Fltr;
+typedef StaticPersistence<> Persistence;
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+ stdoutLog.subscribeTo(RLOG_CHANNEL("info"));
+ //stdoutLog.subscribeTo(RLOG_CHANNEL("rips/info"));
+#endif
+
+ if (argc < 2)
+ {
+ std::cout << "USAGE: avida FILENAME" << std::endl;
+ return 0;
+ }
+
+ AvidaPopulationDetail population(argv[1]);
+ ExplicitDist distances(population);
+
+ RipsGen rips(distances);
+ RipsGen::Evaluator evaluator(rips.distances());
+ rInfo("Max distance: %f", rips.max_distance());
+
+ const AvidaPopulationDetail::OrganismVector& organisms = population.get_organisms();
+ rInfo("Number of organisms: %d", organisms.size());
+ /*
+ for (int i = 0; i < population.get_organisms().size(); ++i)
+ rInfo("%d (%s) %f %d %d", organisms[i].id(),
+ organisms[i].genome().c_str(),
+ organisms[i].fitness(),
+ organisms[i].length(),
+ organisms[i].genome().size());
+ */
+
+ rInfo("Starting to generate rips complex");
+ Fltr f;
+ rips.generate(1, rips.max_distance()/2, make_push_back_functor(f));
+
+ rInfo("Generated Rips complex, filling filtration");
+ f.sort(RipsGen::Comparison(rips.distances()));
+
+ Persistence p(f);
+ p.pair_simplices();
+
+ std::cout << "Outputting histogram of death values" << std::endl;
+ typedef std::vector<RealType> DeathVector;
+ DeathVector deaths;
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(f);
+ for (Persistence::iterator i = p.begin(); i != p.end(); ++i)
+ {
+ if (i->unpaired()) continue;
+ if (i->sign())
+ {
+ const Smplx& s = m[i];
+ const Smplx& t = m[i->pair];
+ AssertMsg(s.dimension() == 0, "Expecting only 0-dimensional diagram");
+ AssertMsg(evaluator(s) == 0, "Expecting only 0 birth values in 0-D diagram ");
+ deaths.push_back(evaluator(t));
+ }
+ }
+
+ // Produce histogram
+ std::sort(deaths.begin(), deaths.end());
+ for (DeathVector::iterator cur = deaths.begin(); cur != deaths.end(); )
+ {
+ DeathVector::iterator nw = std::find_if(cur, deaths.end(),
+ std::bind2nd(std::greater<RealType>(), *cur));
+ std::cout << *cur << "\t" << (nw - cur) << std::endl;
+ cur = nw;
+ }
+ std::cout << "Total: " << deaths.size() + 1; // +1 for the unpaired
+}
--- a/examples/grid/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-set (targets
- test-grid2D
- pdbdistance-vineyard
- combustion-vineyard)
-
-foreach (t ${targets})
- add_executable (${t} ${t}.cpp ${external_sources})
- target_link_libraries (${t} ${libraries} ${cgal_libraries})
-endforeach (t ${targets})
--- a/examples/grid/combustion-vineyard.cpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005
- */
-
-#include <utilities/sys.h>
-#include <utilities/debug.h>
-
-#include <iostream>
-#include <fstream>
-#include <algorithm>
-
-#include "grid2D.h"
-#include "grid2Dvineyard.h"
-
-const int xsize = 600;
-const int ysize = 600;
-const int var = 0; // which variable to use out of nc of them in each record in the file
-
-template<typename T>
-void read(std::ifstream& ifs, T& var)
-{
- ifs.read(reinterpret_cast<char*>(&var), sizeof(T));
-}
-
-int main(int argc, char** argv)
-{
- if (argc < 3)
- {
- std::cout << "Usage: combustion-vineyard FRAME1 FRAME2" << std::endl;
- exit(0);
- }
-
- int size0, nc0;
- int size1, nc1;
-
- std::cout << "Reading: " << argv[1] << std::endl;
- std::ifstream ifs0(argv[1], std::ios::binary);
- std::cout << "Reading: " << argv[2] << std::endl;
- std::ifstream ifs1(argv[2], std::ios::binary);
-
- if (!ifs0 || !ifs1)
- {
- std::cout << "Could not open the frames" << std::endl;
- exit(0);
- }
-
- read(ifs0, size0); read(ifs0, nc0);
- read(ifs1, size1); read(ifs1, nc1);
-
- assert(size0 == size1); assert(nc0 == nc1);
- assert(size0 == xsize*ysize);
-
- Grid2D g0(xsize, ysize), g1(xsize, ysize);
-
- for (int y = 0; y < ysize; ++y)
- for (int x = 0; x < xsize; ++x)
- for (int d = 0; d < nc0; ++d)
- {
- float val0, val1;
- read(ifs0, val0);
- read(ifs1, val1);
- if (d == var)
- {
- g0(x,y) = val0;
- g1(x,y) = val1;
- }
- }
- std::cout << "Grids read" << std::endl;
-
- // Generate filtration and compute pairing
- Grid2DVineyard v(&g0);
- std::cout << "Filtration generated, size: " << v.filtration()->size() << std::endl;
- v.compute_pairing();
- std::cout << "Pairing computed" << std::endl;
-
- // Compute vineyard
- v.compute_vineyard(&g1, true);
- std::cout << "Vineyard computed" << std::endl;
-
- v.vineyard()->save_edges("combustion");
-}
--- a/examples/grid/grid2D.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
- */
-
-#ifndef __GRID2D_H__
-#define __GRID2D_H__
-
-#include <memory>
-#include <vector>
-#include <map>
-#include <set>
-#include <limits>
-//#include <cmath>
-
-#include <boost/serialization/access.hpp>
-#include <boost/serialization/base_object.hpp>
-#include <boost/serialization/split_member.hpp>
-#include <boost/serialization/nvp.hpp>
-
-#include "utilities/types.h"
-
-#include <boost/serialization/export.hpp>
-
-/**
- * Grid2D stores a grid
- */
-class Grid2D
-{
- public:
- typedef RealType ValueType;
- typedef unsigned int CoordinateIndex;
-
- typedef std::vector<ValueType> ValueVector;
-
- public:
- Grid2D(CoordinateIndex xx = 1, CoordinateIndex yy = 1);
-
- /// Sets the grid dimensions to (xx,yy)
- void change_dimensions(CoordinateIndex xx, CoordinateIndex yy);
-
- ValueType& operator()(CoordinateIndex i, CoordinateIndex j) { return data[i*x + j]; }
- const ValueType& operator()(CoordinateIndex i, CoordinateIndex j) const { return data[i*x + j]; }
- ValueType& operator()(CoordinateIndex i) { return data[i]; }
- const ValueType& operator()(CoordinateIndex i) const { return data[i]; }
-
- CoordinateIndex xsize() const { return x; }
- CoordinateIndex ysize() const { return y; }
- CoordinateIndex size() const { return x*y; }
-
- /* Given a sequential index of an element return its coordinates */
- CoordinateIndex xpos(CoordinateIndex i) const { return i / x; }
- CoordinateIndex ypos(CoordinateIndex i) const { return i % x; }
- CoordinateIndex seq(CoordinateIndex i, CoordinateIndex j) const;
-
- std::ostream& operator<<(std::ostream& out) const;
-
- static const CoordinateIndex INVALID_INDEX = -1;
-
- private:
- CoordinateIndex x,y;
- ValueVector data;
-
-#if 0
- private:
- // Serialization
- friend class boost::serialization::access;
-
- template<class Archive> void save(Archive& ar, version_type ) const;
- template<class Archive> void load(Archive& ar, version_type );
-
- BOOST_SERIALIZATION_SPLIT_MEMBER()
-#endif
-};
-//BOOST_CLASS_EXPORT(Grid2D)
-
-
-std::ostream& operator<<(std::ostream& out, const Grid2D& grid) { return grid.operator<<(out); }
-
-
-#include "grid2D.hpp"
-
-#endif // __GRID2D_H__
--- a/examples/grid/grid2D.hpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-#include <iostream>
-#include <limits>
-
-/* Implementations */
-
-Grid2D::
-Grid2D(CoordinateIndex xx, CoordinateIndex yy):
- x(xx), y(yy), data(x*y)
-{}
-
-void
-Grid2D::
-change_dimensions(CoordinateIndex xx, CoordinateIndex yy)
-{
- x = xx; y = yy;
- data.resize(x*y);
-}
-
-Grid2D::CoordinateIndex
-Grid2D::
-seq(CoordinateIndex i, CoordinateIndex j) const
-{
- // Do not forget to check if less than 0, if Index is made signed --- dangerous
- if (i >= x || j >= y)
- return INVALID_INDEX;
-
- return i*x + j;
-}
-
-std::ostream&
-Grid2D::
-operator<<(std::ostream& out) const
-{
- for (Grid2D::CoordinateIndex i = 0; i < xsize(); ++i)
- {
- for (Grid2D::CoordinateIndex j = 0; j < ysize(); ++j)
- std::cout << operator()(i, j) << ' ';
- std::cout << std::endl;
- }
- return out;
-}
-
-#if 0
-using boost::serialization::make_nvp;
-
-template<class Archive>
-void
-Grid2D::
-save(Archive& ar, version_type ) const
-{
- ar << BOOST_SERIALIZATION_NVP(x);
- ar << BOOST_SERIALIZATION_NVP(y);
- ar << make_nvp("data", data);
-}
-
-template<class Archive>
-void
-Grid2D::
-load(Archive& ar, version_type )
-{
- ar >> make_nvp("x", x);
- ar >> make_nvp("y", y);
- ar >> make_nvp("data", data);
-}
-#endif
--- a/examples/grid/grid2Dvineyard.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,198 +0,0 @@
-/*
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
- */
-
-#ifndef __GRID2DVINEYARD_H__
-#define __GRID2DVINEYARD_H__
-
-#include "utilities/sys.h"
-#include "utilities/debug.h"
-
-#include "grid2D.h"
-#include "topology/lowerstarfiltration.h"
-
-#include <CGAL/Kinetic/Inexact_simulation_traits.h>
-#include <CGAL/Kinetic/Sort.h>
-#include <CGAL/Kinetic/Sort_visitor_base.h>
-
-#include <vector>
-
-
-class Grid2DVineyard
-{
- public:
- typedef Grid2DVineyard Self;
-
- class VertexType;
- typedef std::vector<VertexType> VertexVector;
- typedef VertexVector::iterator VertexIndex;
-
- typedef LowerStarFiltration<VertexIndex> LSFiltration;
-
- class StaticEvaluator;
- class KineticEvaluator;
- class VertexComparison;
-
- typedef Grid2D::CoordinateIndex CoordinateIndex;
- typedef Grid2D::ValueType ValueType;
-
- typedef LSFiltration::Index Index;
- typedef LSFiltration::Simplex Simplex;
- typedef LSFiltration::VertexOrderIndex VertexOrderIndex;
- typedef LSFiltration::Vineyard Vineyard;
- typedef Vineyard::Evaluator Evaluator;
-
- class SortVisitor;
- typedef CGAL::Kinetic::Inexact_simulation_traits Traits;
- typedef CGAL::Kinetic::Sort<Traits, SortVisitor> Sort;
- typedef Traits::Simulator Simulator;
- typedef Traits::Active_points_1_table ActivePointsTable;
- typedef ActivePointsTable::Key Key;
- typedef std::map<Key, VertexOrderIndex> KeyOrderMap;
-
- typedef std::vector<Grid2D*> GridStackContainer;
-
- public:
- Grid2DVineyard(Grid2D* g);
- ~Grid2DVineyard();
-
- void compute_pairing();
- void compute_vineyard(Grid2D* grid, bool explicit_events = false);
-
- Grid2D* grid() const { return grid_stack_.back(); }
- Grid2D* grid(int i) const { return grid_stack_[i]; }
- int num_grids() const { return grid_stack_.size(); }
- const LSFiltration* filtration() const { return filtration_; }
- const Vineyard* vineyard() const { return vineyard_; }
-
- public:
- // For Kinetic Sort
- void swap(Key a, Key b);
-
- protected:
- // Do something cleverer
- virtual bool neighbors(VertexIndex v1, VertexIndex v2) const { return true; }
-
- private:
- void add_simplices();
- void change_evaluator(Evaluator* eval);
-
- private:
- GridStackContainer grid_stack_;
- VertexVector vertices_;
- LSFiltration* filtration_;
- Vineyard* vineyard_;
- Evaluator* evaluator_;
-
- KeyOrderMap kinetic_map_;
-
-#if 0
- private:
- // Serialization
- friend class boost::serialization::access;
-
- Grid2DVineyard() {}
-
- template<class Archive>
- void serialize(Archive& ar, version_type )
- {
- ar & BOOST_SERIALIZATION_NVP(grid_stack_);
- ar & BOOST_SERIALIZATION_NVP(vertices_);
- ar & BOOST_SERIALIZATION_NVP(filtration_);
- };
-#endif
-};
-
-//BOOST_CLASS_EXPORT(Grid2DVineyard)
-
-class Grid2DVineyard::VertexType
-{
- public:
- VertexType(CoordinateIndex ii = 0): i_(ii) {}
-
- CoordinateIndex index() const { return i_; }
- void set_index(CoordinateIndex i) { i_ = i; }
- VertexOrderIndex get_order() const { return order_; }
- void set_order(const VertexOrderIndex& o) { order_ = o; }
-
- Key kinetic_key() const { return key_; }
- void set_kinetic_key(Key k) { key_ = k; }
-
- private:
- CoordinateIndex i_;
- VertexOrderIndex order_;
- Key key_;
-};
-
-std::ostream& operator<<(std::ostream& out, const Grid2DVineyard::VertexIndex& vi) { return out << vi->index(); }
-
-class Grid2DVineyard::VertexComparison
-{
- public:
- VertexComparison(const Grid2D* g): grid(g) {}
- bool operator()(VertexIndex i, VertexIndex j) const { return (*grid)(i->index()) <
- (*grid)(j->index()); }
-
- private:
- const Grid2D* grid;
-
-#if 0
- private:
- // Serialization
- friend class boost::serialization::access;
-
- VertexComparison() {}
-
- template<class Archive>
- void serialize(Archive& ar, version_type ) { ar & BOOST_SERIALIZATION_NVP(grid); }
-#endif
-};
-
-class Grid2DVineyard::StaticEvaluator: public Evaluator
-{
- public:
- StaticEvaluator(Grid2D* grid, RealType time):
- time_(time), grid_(grid) {}
-
- virtual RealType time() { return time_; }
- virtual RealType value(const Simplex& s) { return (*grid_)(s.get_attachment()->index()); }
-
- private:
- RealType time_;
- Grid2D* grid_;
-};
-
-class Grid2DVineyard::KineticEvaluator: public Evaluator
-{
- public:
- KineticEvaluator(Simulator::Handle sp, ActivePointsTable::Handle apt, RealType time_offset):
- sp_(sp), apt_(apt), time_offset_(time_offset) {}
-
- virtual RealType time() { return time_offset_ + CGAL::to_double(get_time()); }
- virtual RealType value(const Simplex& s) { return CGAL::to_double(apt_->at(s.get_attachment()->kinetic_key()).x()(get_time())); }
-
- private:
- Simulator::Time get_time() { return sp_->current_time(); }
-
- Simulator::Handle sp_;
- ActivePointsTable::Handle apt_;
- RealType time_offset_;
-};
-
-
-class Grid2DVineyard::SortVisitor: public CGAL::Kinetic::Sort_visitor_base
-{
- public:
- SortVisitor(Grid2DVineyard* gv): gv_(gv) {}
-
- template<class Vertex_handle>
- void before_swap(Vertex_handle a, Vertex_handle b) const { gv_->swap(*a,*b); }
-
- private:
- Grid2DVineyard* gv_;
-};
-
-#include "grid2Dvineyard.hpp"
-
-#endif // __GRID2DVINEYARD_H__
--- a/examples/grid/grid2Dvineyard.hpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/* Implementation */
-
-Grid2DVineyard::
-Grid2DVineyard(Grid2D* g): vertices_(g->size())
-{
- grid_stack_.push_back(g);
- for (CoordinateIndex i = 0; i < g->size(); ++i)
- vertices_[i].set_index(i);
-
- evaluator_ = new StaticEvaluator(grid(), 0);
- vineyard_ = new Vineyard(evaluator_);
-
- filtration_ = new LSFiltration(vertices_.begin(), vertices_.end(), VertexComparison(grid()), vineyard_);
- add_simplices();
-}
-
-Grid2DVineyard::
-~Grid2DVineyard()
-{
- delete filtration_;
- delete vineyard_;
- delete evaluator_;
-}
-
-void
-Grid2DVineyard::
-compute_pairing()
-{
- filtration_->fill_simplex_index_map();
- filtration_->pair_simplices(filtration_->begin(), filtration_->end());
- vineyard_->start_vines(filtration_->begin(), filtration_->end());
-}
-
-void
-Grid2DVineyard::
-compute_vineyard(Grid2D* g, bool explicit_events)
-{
- AssertMsg(filtration_->is_paired(), "Simplices must be paired for a vineyard to be computed");
-
- typedef Traits::Kinetic_kernel::Point_1 Point;
- typedef Traits::Kinetic_kernel::Function_kernel::Construct_function CF;
- typedef Traits::Kinetic_kernel::Motion_function F;
-
- Traits tr(0,1);
- Simulator::Handle sp = tr.simulator_handle();
- ActivePointsTable::Handle apt = tr.active_points_1_table_handle();
- Sort sort(tr, SortVisitor(this));
-
- // Setup the (linear) trajectories
- std::cout << "Setting up trajectories" << std::endl;
- CF cf;
- kinetic_map_.clear();
- for (VertexIndex cur = vertices_.begin(); cur != vertices_.end(); ++cur)
- {
- ValueType val0 = (*grid())(cur->index());
- ValueType val1 = (*g)(cur->index());
- F x = cf(F::NT(val0), F::NT(val1 - val0)); // x = val0 + (val1 - val0)*t
- Point p(x);
- cur->set_kinetic_key(apt->insert(p));
- kinetic_map_[cur->kinetic_key()] = cur->get_order();
- if (cur->index() % 10000 == 0)
- std::cout << "Added trajectory: " << cur->index() << " " << val0 << " " << val1 << std::endl;
- }
-
- // Process all the events (compute the vineyard in the process)
- change_evaluator(new KineticEvaluator(sp, apt, num_grids() - 1));
- if (explicit_events)
- {
- while (sp->next_event_time() < 1)
- {
- std::cout << "Next event time: " << sp->next_event_time() << std::endl;
- sp->set_current_event_number(sp->current_event_number() + 1);
- std::cout << "Processed event" << std::endl;
- }
- } else
- sp->set_current_time(1.0);
- std::cout << "Processed " << sp->current_event_number() << " events" << std::endl;
-
- // Add the grid to the stack
- grid_stack_.push_back(g);
- change_evaluator(new StaticEvaluator(grid(), num_grids() - 1));
- vineyard_->record_diagram(filtration_->begin(), filtration_->end());
-}
-
-void
-Grid2DVineyard::
-swap(Key a, Key b)
-{
- VertexOrderIndex ao = kinetic_map_[a], bo = kinetic_map_[b];
- AssertMsg(filtration_->get_vertex_cmp()(ao, bo), "In swap(a,b), a must precede b");
- filtration_->transpose_vertices(ao);
- AssertMsg(filtration_->get_vertex_cmp()(bo, ao), "In swap(a,b), b must precede a after the transposition");
-}
-
-void
-Grid2DVineyard::
-add_simplices()
-{
- // Takes advantage of LowerStarFiltration's smart append (which allows faces
- // to be inserted after cofaces, since everything is rearranged in the
- // proper lower star order anyway). Also note that vertices were added by
- // LowerStarFiltration's constructor
- for (CoordinateIndex x = 0; x < grid()->xsize() - 1; ++x)
- for (CoordinateIndex y = 0; y < grid()->ysize() - 1; ++y)
- {
- VertexIndex v(&vertices_[grid()->seq(x,y)]);
- VertexIndex vh(&vertices_[grid()->seq(x+1,y)]);
- VertexIndex vv(&vertices_[grid()->seq(x,y+1)]);
- VertexIndex vd(&vertices_[grid()->seq(x+1,y+1)]);
-
- Simplex sh(v);
- sh.add(vh); filtration_->append(sh); // Horizontal edge
- sh.add(vd); filtration_->append(sh); // "Horizontal" triangle
-
- Simplex sv(v);
- sv.add(vv); filtration_->append(sv); // Vertical edge
- sv.add(vd); filtration_->append(sv); // "Vertical" triangle
-
- Simplex sd(v);
- sd.add(vd); filtration_->append(sd); // Diagonal edge
-
- if (y == grid()->ysize() - 2)
- {
- Simplex s(vv);
- s.add(vd); filtration_->append(s); // Top edge
- }
- if (x == grid()->xsize() - 2)
- {
- Simplex s(vh);
- s.add(vd); filtration_->append(s); // Right edge
- }
- }
-}
-
-void
-Grid2DVineyard::
-change_evaluator(Evaluator* eval)
-{
- AssertMsg(evaluator_ != 0, "change_evaluator() assumes that existing evaluator is not null");
-
- delete evaluator_;
- evaluator_ = eval;
- vineyard_->set_evaluator(evaluator_);
-}
-
--- a/examples/grid/pdbdistance-vineyard.cpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-//#include <boost/archive/binary_oarchive.hpp>
-#include "utilities/sys.h"
-#include "utilities/debug.h"
-
-#include "pdbdistance.h"
-#include "grid2Dvineyard.h"
-
-#include <fstream>
-#include <string>
-#include <sstream>
-
-std::string frame_filename(const std::string& prefix, int frame, int subframe)
-{
- std::ostringstream os;
- os << prefix << frame << "_" << subframe << ".pdb";
- return os.str();
-}
-
-int main(int argc, char** argv)
-{
-#ifdef CWDEBUG
- Debug(dc::filtration.off());
- Debug(dc::cycle.off());
- Debug(dc::vineyard.off());
- Debug(dc::transpositions.off());
- Debug(dc::lsfiltration.off());
-
- dionysus::debug::init();
-#endif
-
- if (argc < 5)
- {
- std::cout << "Usage: pdbdistance FILENAME LASTFRAME LASTSUBFRAME OUTFILENAME [CAs_ONLY]" << std::endl;
- std::cout << " FILENAME - prefix of the filenames of the PDB frames" << std::endl;
- std::cout << " LASTFRAME - the last frame number" << std::endl;
- std::cout << " LASTSUBFRAME - the last subframe number" << std::endl;
- std::cout << " OUTFILENAME - filename prefix for the resulting vineyards" << std::endl;
- std::cout << " CAs_ONLY - only use alpha carbons [1 = true, 0 = false, default: 1]" << std::endl;
- std::cout << std::endl;
- std::cout << "Computes a vineyard of the pairwise distance function for a sequence of PDB frames." << std::endl;
- std::cout << "Frames are in files FILENAME#1_#2.pdb, where #1 goes from 0 to LASTFRAME, " << std::endl;
- std::cout << "and #2 goes from 0 to LASTSUBFRAME." << std::endl;
- exit(0);
- }
- std::string infilename = argv[1];
- int lastframe; std::istringstream(argv[2]) >> lastframe;
- int lastsubframe; std::istringstream(argv[3]) >> lastsubframe;
- std::string outfilename = argv[4];
- bool cas_only = true;
- if (argc > 5)
- std::istringstream(argv[5]) >> cas_only;
-
- // Compute initial filtration
- int f = 0; int sf = 0;
- std::ifstream in(frame_filename(infilename, f, sf++).c_str());
- Grid2DVineyard v(new PDBDistanceGrid(in, cas_only));
- in.close();
- std::cout << "Filtration generated, size: " << v.filtration()->size() << std::endl;
- v.compute_pairing();
- std::cout << "Pairing computed" << std::endl;
-
- // Process frames computing the vineyard
- while (f <= lastframe)
- {
- std::string fn = frame_filename(infilename, f, sf++);
- std::cout << "Processing " << fn << std::endl;
- in.open(fn.c_str());
- v.compute_vineyard(new PDBDistanceGrid(in, cas_only));
- in.close();
- if (sf == lastsubframe) { sf = 0; ++f; }
- }
- std::cout << "Vineyard computed" << std::endl;
-
- v.vineyard()->save_edges(outfilename);
-
-#if 0
- std::ofstream ofs(outfilename.c_str(), std::ios::binary);
- boost::archive::binary_oarchive oa(ofs);
- oa << make_nvp("Filtration", pgf);
- ofs.close();
-#endif
-}
--- a/examples/grid/pdbdistance.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
- *
- * Depends on Daniel Russel's "simple C++ PDB" (aka DSR-PDB).
- */
-
-#ifndef __PDBDISTANCE_H__
-#define __PDBDISTANCE_H__
-
-#include <fstream>
-#include <string>
-#include <dsrpdb/Protein.h>
-#include <dsrpdb/iterator.h>
-#include <cmath>
-
-#include <boost/serialization/access.hpp>
-
-#include "utilities/types.h"
-#include "grid2D.h"
-
-#include <boost/serialization/export.hpp>
-
-
-class PDBDistanceGrid: public Grid2D
-{
- public:
- PDBDistanceGrid()
- {}
-
- PDBDistanceGrid(std::istream& in, bool ca_only = true)
- {
- load_stream(in, ca_only);
- }
-
- void load_stream(std::istream& in, bool ca_only = true)
- {
- dsrpdb::Protein p(in);
- typedef std::vector<dsrpdb::Point> PointVector;
- PointVector coordinates;
- if (ca_only)
- {
- PointVector v(ca_coordinates_begin(p), ca_coordinates_end(p));
- coordinates.swap(v);
- }
- else
- {
- PointVector v(backbone_coordinates_begin(p), backbone_coordinates_end(p));
- coordinates.swap(v);
- }
-
- std::cout << "Coordinatess created, size: " << coordinates.size() << std::endl;
-
- Grid2D::change_dimensions(coordinates.size(), coordinates.size());
- for (Grid2D::CoordinateIndex i = 0; i < coordinates.size(); ++i)
- for (Grid2D::CoordinateIndex j = 0; j < coordinates.size(); ++j)
- {
- if (i < j)
- Grid2D::operator()(i,j) = distance(coordinates[i], coordinates[j]);
- else
- Grid2D::operator()(i,j) = 0;
- }
- }
-
- private:
- Grid2D::ValueType distance(dsrpdb::Point p1, dsrpdb::Point p2) const
- {
- dsrpdb::Vector v = p1 - p2;
- return std::sqrt(v*v);
- }
-
-#if 0
- private:
- // Serialization
- friend class boost::serialization::access;
-
- template<class Archive>
- void serialize(Archive& ar, version_type version)
- {
- ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Grid2D);
- }
-#endif
-};
-
-//BOOST_CLASS_EXPORT(PDBDistanceGrid)
-
-#endif // __PDBDISTANCE_H__
--- a/examples/grid/test-grid2D.cpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#include "grid2D.h"
-#include <iostream>
-
-int main()
-{
- Grid2D grid(40,60);
- int i = 0;
- for (int x = 0; x < 40; ++x)
- for (int y = 0; y < 60; ++y)
- {
- grid(x,y) = i++;
- }
-
- std::cout << grid(20,30) << std::endl;
- std::cout << grid << std::endl;
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/homology-zigzags/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,11 @@
+set (targets
+ rips-pairwise
+ M-ZZ
+ dM-ZZ
+ iR-ZZ
+ oR-ZZ)
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/homology-zigzags/M-ZZ.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,432 @@
+/***************************************************************************
+
+ M-ZZ: Morozov zigzag implementation
+ Copyright (C) 2009-2012 Dmitriy Morozov
+
+ Adapted from its original version ("rips-zigzag.cpp" in
+ the Dionysus library) by Steve Oudot (2012).
+
+ Changelog (2012-11-22):
+
+ - the barcode is now output on a log scale and in a format that is
+ compatible with the Matlab layer of the PLEX 2.5 library,
+
+ - the barcode representation is now by closed intervals instead of
+ half-open intervals.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+***************************************************************************/
+
+#include <topology/rips.h>
+#include <topology/zigzag-persistence.h>
+#include <utilities/types.h>
+#include <utilities/containers.h>
+
+#include <geometry/l2distance.h> // Point, PointContainer, L2DistanceType, read_points
+#include <geometry/distances.h>
+
+#include <utilities/log.h>
+#include <utilities/memory.h> // for report_memory()
+#include <utilities/timer.h>
+
+#include <map>
+#include <cmath>
+#include <fstream>
+#include <stack>
+#include <cstdlib>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#ifdef COUNTERS
+static Counter* cComplexSize = GetCounter("rips/size");
+static Counter* cOperations = GetCounter("rips/operations");
+#endif // COUNTERS
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+
+typedef PairDistances::IndexType Vertex;
+typedef Simplex<Vertex> Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef std::list<Smplx> SimplexList;
+typedef std::set<Smplx, Smplx::VertexDimensionComparison> SimplexSet;
+
+typedef std::vector<Vertex> VertexVector;
+typedef std::vector<DistanceType> EpsilonVector;
+typedef std::vector<std::pair<Vertex, Vertex> > EdgeVector;
+
+typedef Rips<PairDistances, Smplx> RipsGenerator;
+typedef RipsGenerator::Evaluator SimplexEvaluator;
+
+struct BirthInfo;
+typedef ZigzagPersistence<BirthInfo> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef std::map<Smplx, Index,
+ Smplx::VertexDimensionComparison> Complex;
+typedef Zigzag::ZColumn Boundary;
+
+typedef std::vector<std::list<std::pair<double, double> > > IntervalsVector;
+
+// Information we need to know when a class dies
+struct BirthInfo
+{
+ BirthInfo(DistanceType dist = DistanceType(), Dimension dim = Dimension()):
+ distance(dist), dimension(dim) {}
+ DistanceType distance;
+ Dimension dimension;
+};
+
+// Forward declarations of auxilliary functions
+// Note: min_value is used only for log-scale, so set it up to zero by default
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value=0);
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension);
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b);
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi);
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& multiplier,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Timer total, remove, add, ec, vc;
+ total.start();
+
+#if 0
+ SetFrequency(cOperations, 25000);
+ SetTrigger(cOperations, cComplexSize);
+#endif
+
+ unsigned skeleton_dimension;
+ float multiplier;
+ std::string infilename, outfilename;
+ bool logscale = false;
+ process_command_line_options(argc, argv, skeleton_dimension, multiplier, logscale, infilename, outfilename);
+
+ // Read in points
+ PointContainer points;
+ read_points(infilename, points);
+
+ std::cout << "Number of points: " << points.size() << std::endl;
+
+ // Create output file
+ std::ofstream out(outfilename.c_str());
+
+ // Create pairwise distances
+ PairDistances distances(points);
+
+ // Create intervals DS
+ IntervalsVector intervals(skeleton_dimension);
+ // for (int i=0; i<skeleton_dimension; i++)
+ // intervals[i] = new std::list<std::pair<double, double> > ();
+
+ // Order vertices and epsilons (in maxmin fashion)
+ VertexVector vertices;
+ EpsilonVector epsilons;
+ EdgeVector edges;
+
+ {
+ EpsilonVector dist(distances.size(), Infinity);
+
+ vertices.push_back(distances.begin());
+ //epsilons.push_back(Infinity);
+ while (vertices.size() < distances.size())
+ {
+ for (Vertex v = distances.begin(); v != distances.end(); ++v)
+ dist[v] = std::min(dist[v], distances(v, vertices.back()));
+ EpsilonVector::const_iterator max = std::max_element(dist.begin(), dist.end());
+ vertices.push_back(max - dist.begin());
+ epsilons.push_back(*max);
+ }
+ epsilons.push_back(0);
+ }
+
+ rInfo("Point and epsilon ordering:");
+ for (unsigned i = 0; i < vertices.size(); ++i)
+ rInfo(" %4d: %4d - %f", i, vertices[i], epsilons[i]);
+
+ // Generate and sort all the edges
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ {
+ Vertex u = vertices[i];
+ Vertex v = vertices[j];
+ if (distances(u,v) <= multiplier*epsilons[j-1])
+ edges.push_back(std::make_pair(u,v));
+ }
+ std::sort(edges.begin(), edges.end(), RipsGenerator::ComparePair(distances));
+ std::cout << "Total participating edges: " << edges.size() << std::endl;
+ for (EdgeVector::const_iterator cur = edges.begin(); cur != edges.end(); ++cur)
+ rDebug(" (%d, %d) %f", cur->first, cur->second, distances(cur->first, cur->second));
+
+ // Construct zigzag
+ Complex complex;
+ Zigzag zz;
+ RipsGenerator rips(distances);
+ SimplexEvaluator size(distances);
+
+ // Insert vertices (initial complex is just a disjoint union of vertices)
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ {
+ // Add a vertex
+ Smplx sv; sv.add(vertices[i]);
+ rDebug("Adding %s", tostring(sv).c_str());
+ add.start();
+ complex.insert(std::make_pair(sv,
+ zz.add(Boundary(),
+ BirthInfo(0, 0)).first));
+ add.stop();
+ //rDebug("Newly born cycle order: %d", complex[sv]->low->order);
+ CountNum(cComplexSize, 0);
+ Count(cComplexSize);
+ Count(cOperations);
+ }
+
+ rInfo("Commencing computation");
+ boost::progress_display show_progress(vertices.size());
+ unsigned ce = 0; // index of the current one past last edge in the complex
+ for (unsigned stage = 0; stage != vertices.size() - 1; ++stage)
+ {
+ unsigned i = vertices.size() - 1 - stage;
+ // std::cout << "Current stage " << stage << " "
+ // << vertices[i] << " " << epsilons[i-1] << ": "
+ // << multiplier*epsilons[i-1] << std::endl;
+
+ /* Increase epsilon */
+ // Record the cofaces of all the simplices that need to be removed and reinserted
+ SimplexSet cofaces;
+ rDebug(" Cofaces size: %d", cofaces.size());
+
+ // Add anything else that needs to be inserted into the complex
+ while (ce < edges.size())
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[ce];
+ if (distances(u,v) <= multiplier*epsilons[i-1])
+ ++ce;
+ else
+ break;
+ rDebug(" Recording cofaces of edges[%d]=(%d, %d) with size=%f", (ce-1), u, v, distances(u,v));
+ ec.start();
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ ec.stop();
+ }
+ rDebug(" Recorded new cofaces to add");
+
+ // Insert all the cofaces
+ rDebug(" Cofaces size: %d", cofaces.size());
+ for (SimplexSet::const_iterator cur = cofaces.begin(); cur != cofaces.end(); ++cur)
+ {
+ Index idx; Death d; Boundary b;
+ rDebug(" Adding %s, its size %f", tostring(*cur).c_str(), size(*cur));
+ make_boundary(*cur, complex, zz, b);
+ add.start();
+ boost::tie(idx, d) = zz.add(b,
+ BirthInfo(epsilons[i-1], cur->dimension()));
+ add.stop();
+ //if (!d) rDebug("Newly born cycle order: %d", complex[*cur]->low->order);
+ CountNum(cComplexSize, cur->dimension());
+ Count(cComplexSize);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ complex.insert(std::make_pair(*cur, idx));
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rInfo("Increased epsilon; complex size: %d", complex.size());
+ report_memory();
+
+ /* Remove the vertex */
+ cofaces.clear();
+ rDebug(" Cofaces size: %d", cofaces.size());
+ vc.start();
+ rips.vertex_cofaces(vertices[i],
+ skeleton_dimension,
+ multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ vc.stop();
+ rDebug(" Computed cofaces of the vertex, their number: %d", cofaces.size());
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing: %s", tostring(*cur).c_str());
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ for (Complex::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ rDebug(" %s", tostring(cur->first).c_str());
+ report_memory();
+
+ ++show_progress;
+ }
+
+ // Remove the last vertex
+ AssertMsg(complex.size() == 1, "Only one vertex must remain");
+ remove.start();
+ Death d = zz.remove(complex.begin()->second, BirthInfo(epsilons[0], -1));
+ remove.stop();
+ complex.erase(complex.begin());
+ if (!d) AssertMsg(false, "The vertex must have died");
+ report_death(intervals, d, epsilons[0], epsilons[0], skeleton_dimension);
+ CountNumBy(cComplexSize, 0, -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ ++show_progress;
+
+ total.stop();
+
+ remove.check("Remove timer ");
+ add.check ("Add timer ");
+ ec.check ("Edge coface timer ");
+ vc.check ("Vertex coface timer ");
+ total.check ("Total timer ");
+
+ std::cout << "Writing intervals...";
+ // Note (hack): use epsilons[vertices.size()-2]/2 as minimal value for the log-scale intervals to avoid intervals starting at -infinity and thus a scaling effect
+ write_intervals(out, intervals, skeleton_dimension, logscale, epsilons[vertices.size()-2]/2);
+ std::cout << " done." << std::endl;
+}
+
+
+const double LOG2 = std::log(2.0);
+double log2(double x) {
+ return std::log(x) / LOG2;
+}
+
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value) {
+ out << "I = { ";
+ for (int d = 0; d<skeleton_dimension; d++) {
+ out << "[ ";
+ for (std::list<std::pair<double,double> >::const_iterator pit = intervals[d].begin(); pit != intervals[d].end(); pit++)
+ if (logscale)
+ out << "[" << log2(std::max(pit->first, min_value)) << ";" << log2(std::max(pit->second, min_value)) << "] ";
+ else
+ out << "[" << pit->first << ";" << pit->second << "] ";
+
+ // add dummy interval if intervals list is empty
+ if (intervals[d].empty())
+ out << "[0;0] ";
+ out << "] ,";
+ }
+ out << "} ";
+}
+
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension)
+{
+ // std::cerr << " d = " << d;
+ // if (d)
+ // std::cerr << " d->dim = " << d->dimension
+ // << " d->dist = " << d->distance;
+ // std::cerr << " epsilon = " << epsilon << std::endl;
+
+ if (d && ((d->distance - epsilon) != 0) && (d->dimension < skeleton_dimension))
+ intervals[d->dimension].push_back(std::pair<double,double>(d->distance, birthEpsilon));
+}
+
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b)
+{
+ rDebug(" Boundary of <%s>", tostring(s).c_str());
+ for (Smplx::BoundaryIterator cur = s.boundary_begin(); cur != s.boundary_end(); ++cur)
+ {
+ b.append(c[*cur], zz.cmp);
+ rDebug(" %d", c[*cur]->order);
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi)
+{ return (out << bi.distance); }
+
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& multiplier,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Location to save persistence pairs");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("dim,d", po::value<unsigned>(&skeleton_dimension)->default_value(3), "maximal dimension of the Rips complex")
+ ("rho,r", po::value<float>(&multiplier)->default_value(3), "multiplier rho used in the Rips parameter")
+ ( "logscale,l" , po::value( &logscale )->zero_tokens(), "outputs interval bounds on log_2 scale" ) ;
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file output-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/homology-zigzags/README Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,43 @@
+********************************************************************************
+* Rips-ZZ: Rips Zigzags for Homology Inference *
+********************************************************************************
+
+This is an extension to the Rips package of the Dionysus library. It
+adapts the code of the Morozov zigzag (M-ZZ) and image Rips zigzag
+(iR-ZZ) to the context of the following paper:
+
+Title: Zigzag Zoology: Rips Zigzags for Homology Inference
+Authors: Steve Y. Oudot and Donald R. Sheehy
+
+It also provides two variants of the M-ZZ: the discretized Morozov
+zigzag (dM-ZZ) and the oscillating Rips zigzag (oR-ZZ).
+
+
+Source files:
+
+ - M-ZZ.cpp: implementation of the Morozov zigzag
+ - dM-ZZ.cpp: implementation of the discretized Morozov zigzag
+ - oR-ZZ.cpp: implementation of the oscillating Rips zigzag
+ - iR-ZZ.cpp: implementation of the image Rips zigzag
+ - rips-pairwise.cpp: computation of the standard Rips filtration
+ and its persistent homology.
+
+Execution:
+
+ - the list of arguments required by a program can be obtained by
+ running that program without arguments.
+
+ - every program takes in a point cloud file, in xyz... format (no
+ need to indicate the ambient dimension at the beginning of the
+ file. A sample point cloud is provided in the file
+ input/spiral_3d_10k.xyz
+
+ - every program also asks for the name of the output file in which
+ the barcode will be written. The output format is such that the
+ file can be executed in Matlab. This creates a cells structures
+ that can be read by the px_homologyplot function from the PLEX 2.5
+ library (a copy of which is provided). This function will plot the
+ barcode dimension per dimension.
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/homology-zigzags/dM-ZZ.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,451 @@
+/***************************************************************************
+
+ dM-ZZ: discretized Morozov zigzag implementation
+ Copyright (C) 2012 Steve Oudot
+
+ Adapted from the Morozov zigzag implementation provided in the
+ Rips package of the Dionysus library (see the file "M-ZZ.cpp").
+ The Dionysus library is (C) 2006-2012 Dmitriy Morozov.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+***************************************************************************/
+
+#include <topology/rips.h>
+#include <topology/zigzag-persistence.h>
+#include <utilities/types.h>
+#include <utilities/containers.h>
+
+#include <geometry/l2distance.h> // Point, PointContainer, L2DistanceType, read_points
+#include <geometry/distances.h>
+
+#include <utilities/log.h>
+#include <utilities/memory.h> // for report_memory()
+#include <utilities/timer.h>
+
+#include <map>
+#include <cmath>
+#include <fstream>
+#include <stack>
+#include <cstdlib>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#ifdef COUNTERS
+static Counter* cComplexSize = GetCounter("rips/size");
+static Counter* cOperations = GetCounter("rips/operations");
+#endif // COUNTERS
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+
+typedef PairDistances::IndexType Vertex;
+typedef Simplex<Vertex> Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef std::list<Smplx> SimplexList;
+typedef std::set<Smplx, Smplx::VertexDimensionComparison> SimplexSet;
+
+typedef std::vector<Vertex> VertexVector;
+typedef std::vector<DistanceType> EpsilonVector;
+typedef std::vector<std::pair<Vertex, Vertex> > EdgeVector;
+
+typedef Rips<PairDistances, Smplx> RipsGenerator;
+typedef RipsGenerator::Evaluator SimplexEvaluator;
+
+struct BirthInfo;
+typedef ZigzagPersistence<BirthInfo> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef std::map<Smplx, Index,
+ Smplx::VertexDimensionComparison> Complex;
+typedef Zigzag::ZColumn Boundary;
+
+typedef std::vector<std::list<std::pair<double, double> > > IntervalsVector;
+
+// Information we need to know when a class dies
+struct BirthInfo
+{
+ BirthInfo(DistanceType dist = DistanceType(), Dimension dim = Dimension()):
+ distance(dist), dimension(dim) {}
+ DistanceType distance;
+ Dimension dimension;
+};
+
+// Forward declarations of auxilliary functions
+// Note: min_value is used only for log-scale, so set it up to zero by default
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value=0);
+// void report_death(std::ostream& out, Death d, DistanceType epsilon, Dimension skeleton_dimension);
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension);
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b);
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi);
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& multiplier,
+ float& discretization_factor,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Timer total, remove, add, ec, vc;
+ total.start();
+
+#if 0
+ SetFrequency(cOperations, 25000);
+ SetTrigger(cOperations, cComplexSize);
+#endif
+
+ unsigned skeleton_dimension;
+ float multiplier;
+ float discretization_factor;
+ std::string infilename, outfilename;
+ bool logscale;
+ process_command_line_options(argc, argv, skeleton_dimension, multiplier, discretization_factor, logscale, infilename, outfilename);
+
+ // Read in points
+ PointContainer points;
+ read_points(infilename, points);
+
+ std::cout << "Number of points: " << points.size() << std::endl;
+
+ // Create output file
+ std::ofstream out(outfilename.c_str());
+
+ // Create pairwise distances
+ PairDistances distances(points);
+
+ // Create intervals DS
+ IntervalsVector intervals(skeleton_dimension);
+ // for (int i=0; i<skeleton_dimension; i++)
+ // intervals[i] = new std::list<std::pair<double, double> > ();
+
+ // Order vertices and epsilons (in maxmin fashion)
+ VertexVector vertices;
+ EpsilonVector epsilons;
+ EdgeVector edges;
+
+ {
+ EpsilonVector dist(distances.size(), Infinity);
+
+ vertices.push_back(distances.begin());
+ //epsilons.push_back(Infinity);
+ while (vertices.size() < distances.size())
+ {
+ for (Vertex v = distances.begin(); v != distances.end(); ++v)
+ dist[v] = std::min(dist[v], distances(v, vertices.back()));
+ EpsilonVector::const_iterator max = std::max_element(dist.begin(), dist.end());
+ vertices.push_back(max - dist.begin());
+ epsilons.push_back(*max);
+ }
+ epsilons.push_back(0);
+ }
+
+ // Discretize sequence
+ unsigned anchor[vertices.size()];
+ anchor[0] = 0;
+ unsigned ref = 0;
+ // std::cout << "Anchors: 0 (" << epsilons[0] << ")";
+ for (unsigned i=0; i<vertices.size(); i++)
+ if (epsilons[i] < epsilons[anchor[ref]]*discretization_factor) {
+ anchor[i] = i;
+ ref = i;
+ // std::cout << " " << i << " (" << epsilons[i] << ")";
+ }
+ else
+ anchor[i] = ref;
+ // std::cout << std::endl;
+
+
+ rInfo("Point and epsilon ordering:");
+ for (unsigned i = 0; i < vertices.size(); ++i)
+ rInfo(" %4d: %4d - %f", i, vertices[i], epsilons[i]);
+
+ // Generate and sort all the edges
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ {
+ Vertex u = vertices[i];
+ Vertex v = vertices[j];
+ if (distances(u,v) <= multiplier*epsilons[anchor[j-1]])
+ edges.push_back(std::make_pair(u,v));
+ }
+ std::sort(edges.begin(), edges.end(), RipsGenerator::ComparePair(distances));
+ std::cout << "Total participating edges: " << edges.size() << std::endl;
+ for (EdgeVector::const_iterator cur = edges.begin(); cur != edges.end(); ++cur)
+ rDebug(" (%d, %d) %f", cur->first, cur->second, distances(cur->first, cur->second));
+
+ // Construct zigzag
+ Complex complex;
+ Zigzag zz;
+ RipsGenerator rips(distances);
+ SimplexEvaluator size(distances);
+
+ // Insert vertices (initial complex is just a disjoint union of vertices)
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ {
+ // Add a vertex
+ Smplx sv; sv.add(vertices[i]);
+ rDebug("Adding %s", tostring(sv).c_str());
+ add.start();
+ complex.insert(std::make_pair(sv,
+ zz.add(Boundary(),
+ BirthInfo(0, 0)).first));
+ add.stop();
+ //rDebug("Newly born cycle order: %d", complex[sv]->low->order);
+ CountNum(cComplexSize, 0);
+ Count(cComplexSize);
+ Count(cOperations);
+ }
+
+ rInfo("Commencing computation");
+ boost::progress_display show_progress(vertices.size());
+ unsigned ce = 0; // index of the current one past last edge in the complex
+ for (unsigned stage = vertices.size()-1; stage > 0; stage = anchor[stage-1])
+ {
+ // std::cout << "Current stage " << stage << ": "
+ // << anchor[stage-1] << " " << epsilons[anchor[stage-1]] << " "
+ // << multiplier*epsilons[anchor[stage-1]] << std::endl;
+
+ /* Increase epsilon */
+ // Record the cofaces of all the simplices that need to be removed and r`einserted
+ SimplexSet cofaces;
+ rDebug(" Cofaces size: %d", cofaces.size());
+
+ // Add anything else that needs to be inserted into the complex
+ while (ce < edges.size())
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[ce];
+ if (distances(u,v) <= multiplier*epsilons[anchor[stage-1]])
+ ++ce;
+ else
+ break;
+ rDebug(" Recording cofaces of edges[%d]=(%d, %d) with size=%f", (ce-1), u, v, distances(u,v));
+ ec.start();
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ multiplier*epsilons[anchor[stage-1]],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + stage + 1);
+ ec.stop();
+ }
+ rDebug(" Recorded new cofaces to add");
+
+ // Insert all the cofaces
+ rDebug(" Cofaces size: %d", cofaces.size());
+ for (SimplexSet::const_iterator cur = cofaces.begin(); cur != cofaces.end(); ++cur)
+ {
+ Index idx; Death d; Boundary b;
+ rDebug(" Adding %s, its size %f", tostring(*cur).c_str(), size(*cur));
+ make_boundary(*cur, complex, zz, b);
+ add.start();
+ boost::tie(idx, d) = zz.add(b,
+ BirthInfo(epsilons[anchor[stage-1]], cur->dimension()));
+ add.stop();
+ //if (!d) rDebug("Newly born cycle order: %d", complex[*cur]->low->order);
+ CountNum(cComplexSize, cur->dimension());
+ Count(cComplexSize);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ complex.insert(std::make_pair(*cur, idx));
+ report_death(intervals, d, epsilons[anchor[stage-1]], epsilons[stage], skeleton_dimension);
+ }
+ rInfo("Increased epsilon; complex size: %d", complex.size());
+ report_memory();
+
+ /* Remove the vertices */
+ for (unsigned i = stage; i>anchor[stage-1]; i--) {
+ // std::cout << "removing vertex " << i << std::endl;
+ cofaces.clear();
+ rDebug(" Cofaces size: %d", cofaces.size());
+ vc.start();
+ rips.vertex_cofaces(vertices[i],
+ skeleton_dimension,
+ multiplier*epsilons[anchor[stage-1]],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ vc.stop();
+ rDebug(" Computed cofaces of the vertex, their number: %d", cofaces.size());
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing: %s", tostring(*cur).c_str());
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[anchor[stage-1]], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(intervals, d, epsilons[anchor[stage-1]], epsilons[stage], skeleton_dimension);
+ }
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ for (Complex::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ rDebug(" %s", tostring(cur->first).c_str());
+ report_memory();
+ ++show_progress;
+ }
+ }
+
+ // Remove the last vertex
+ // std::cout << "removing vertex 0" << std::endl;
+ if (complex.size() != 1) {
+ std::cerr << "Error: Only one vertex must remain" << std::endl;
+ abort();
+ }
+ remove.start();
+ Death d = zz.remove(complex.begin()->second, BirthInfo(epsilons[0], -1));
+ remove.stop();
+ complex.erase(complex.begin());
+ if (!d) AssertMsg(false, "The vertex must have died");
+ report_death(intervals, d, epsilons[0], epsilons[0], skeleton_dimension);
+ CountNumBy(cComplexSize, 0, -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ ++show_progress;
+
+ total.stop();
+
+ remove.check("Remove timer ");
+ add.check ("Add timer ");
+ ec.check ("Edge coface timer ");
+ vc.check ("Vertex coface timer ");
+ total.check ("Total timer ");
+
+ std::cout << "Writing intervals...";
+ // Note (hack): use epsilons[vertices.size()-2]/2 as minimal value for the log-scale intervals to avoid intervals starting at -infinity and thus a scaling effect
+ write_intervals(out, intervals, skeleton_dimension, logscale, epsilons[vertices.size()-2]/2);
+ std::cout << " done." << std::endl;
+}
+
+
+const double LOG2 = std::log(2.0);
+double log2(double x) {
+ return std::log(x) / LOG2;
+}
+
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value) {
+ out << "I = { ";
+ for (int d = 0; d<skeleton_dimension; d++) {
+ out << "[ ";
+ for (std::list<std::pair<double,double> >::const_iterator pit = intervals[d].begin(); pit != intervals[d].end(); pit++)
+ if (logscale)
+ out << "[" << log2(std::max(pit->first, min_value)) << ";" << log2(std::max(pit->second, min_value)) << "] ";
+ else
+ out << "[" << pit->first << ";" << pit->second << "] ";
+
+ // add dummy interval if intervals list is empty
+ if (intervals[d].empty())
+ out << "[0;0] ";
+ out << "] ,";
+ }
+ out << "} ";
+}
+
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension)
+{
+ // std::cerr << " d = " << d;
+ // if (d)
+ // std::cerr << " d->dim = " << d->dimension
+ // << " d->dist = " << d->distance;
+ // std::cerr << " epsilon = " << epsilon << std::endl;
+
+ if (d && ((d->distance - epsilon) != 0) && (d->dimension < skeleton_dimension))
+ intervals[d->dimension].push_back(std::pair<double,double>(d->distance, birthEpsilon));
+}
+
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b)
+{
+ rDebug(" Boundary of <%s>", tostring(s).c_str());
+ for (Smplx::BoundaryIterator cur = s.boundary_begin(); cur != s.boundary_end(); ++cur)
+ {
+ b.append(c[*cur], zz.cmp);
+ rDebug(" %d", c[*cur]->order);
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi)
+{ return (out << bi.distance); }
+
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& multiplier,
+ float& discretization_factor,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Location to save persistence pairs");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("dim,d", po::value<unsigned>(&skeleton_dimension)->default_value(3), "maximal dimension of the Rips complex")
+ ("rho,r", po::value<float>(&multiplier)->default_value(3), "multiplier rho used in the Rips parameter")
+ ("zeta,z", po::value<float>(&discretization_factor)->default_value(1), "geometric scale gap in the discretization (cst<=1)")
+ ( "logscale,l" , po::value( &logscale )->zero_tokens(), "outputs interval bounds on log_2 scale" ) ;
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file output-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/homology-zigzags/iR-ZZ.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,525 @@
+/***************************************************************************
+
+ iR-ZZ: image Rips zigzag implementation
+ Copyright (C) 2009-2012 Dmitriy Morozov
+
+ Adapted from its original version ("rips-image-zigzag.cpp" in
+ the Dionysus library) by Steve Oudot (2012).
+
+ Changelog (2012-11-22):
+
+ - the barcode is now output on a log scale and in a format that is
+ compatible with the Matlab layer of the PLEX 2.5 library,
+
+ - the barcode representation is now by closed intervals instead of
+ half-open intervals.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+***************************************************************************/
+
+#include <topology/rips.h>
+#include <topology/image-zigzag-persistence.h>
+#include <utilities/types.h>
+#include <utilities/containers.h>
+
+#include <utilities/log.h>
+#include <utilities/memory.h> // for report_memory()
+#include <utilities/timer.h>
+
+#include <geometry/l2distance.h> // for L2Distance and read_points()
+#include <geometry/distances.h>
+
+#include <map>
+#include <cmath>
+#include <fstream>
+#include <stack>
+#include <cstdlib>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+
+#ifdef COUNTERS
+static Counter* cComplexSize = GetCounter("rips/size");
+static Counter* cOperations = GetCounter("rips/operations");
+#endif // COUNTERS
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+
+typedef PairDistances::IndexType Vertex;
+typedef Simplex<Vertex> Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef std::list<Smplx> SimplexList;
+typedef std::set<Smplx, Smplx::VertexDimensionComparison> SimplexSet;
+
+typedef std::vector<Vertex> VertexVector;
+typedef std::vector<DistanceType> EpsilonVector;
+typedef std::vector<std::pair<Vertex, Vertex> > EdgeVector;
+
+typedef Rips<PairDistances, Smplx> RipsGenerator;
+typedef RipsGenerator::Evaluator SimplexEvaluator;
+
+struct BirthInfo;
+typedef ImageZigzagPersistence<BirthInfo> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef std::map<Smplx, Index,
+ Smplx::VertexDimensionComparison> Complex;
+typedef Zigzag::ZColumn Boundary;
+
+typedef std::vector<std::list<std::pair<double, double> > > IntervalsVector;
+
+// Information we need to know when a class dies
+struct BirthInfo
+{
+ BirthInfo(DistanceType dist = DistanceType(), Dimension dim = Dimension()):
+ distance(dist), dimension(dim) {}
+ DistanceType distance;
+ Dimension dimension;
+};
+
+// Forward declarations of auxilliary functions
+// Note: min_value is used only for log-scale, so set it up to zero by default
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value=0);
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension);
+// void report_death(std::ofstream& out, Death d, DistanceType epsilon, Dimension skeleton_dimension);
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b);
+void show_image_betti(Zigzag& zz, Dimension skeleton);
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi);
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& from_multiplier,
+ float& to_multiplier,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Timer total, remove, add, ec, vc;
+ total.start();
+
+#if 0
+ SetFrequency(cOperations, 25000);
+ SetTrigger(cOperations, cComplexSize);
+#endif
+
+ unsigned skeleton_dimension;
+ float from_multiplier, to_multiplier;
+ std::string infilename, outfilename;
+ bool logscale = false;
+ process_command_line_options(argc, argv, skeleton_dimension, from_multiplier, to_multiplier, logscale, infilename, outfilename);
+
+ // Read in points
+ PointContainer points;
+ read_points(infilename, points);
+
+ // Create output file
+ std::ofstream out(outfilename.c_str());
+
+ // Create pairwise distances
+ PairDistances distances(points);
+
+ // Create intervals DS
+ IntervalsVector intervals(skeleton_dimension);
+ // for (int i=0; i<skeleton_dimension; i++)
+ // intervals[i] = new std::list<std::pair<double, double> > ();
+
+ // Order vertices and epsilons (in maxmin fashion)
+ VertexVector vertices;
+ EpsilonVector epsilons;
+ EdgeVector edges;
+
+ {
+ EpsilonVector dist(distances.size(), Infinity);
+
+ vertices.push_back(distances.begin());
+ //epsilons.push_back(Infinity);
+ while (vertices.size() < distances.size())
+ {
+ for (Vertex v = distances.begin(); v != distances.end(); ++v)
+ dist[v] = std::min(dist[v], distances(v, vertices.back()));
+ EpsilonVector::const_iterator max = std::max_element(dist.begin(), dist.end());
+ vertices.push_back(max - dist.begin());
+ epsilons.push_back(*max);
+ }
+ epsilons.push_back(0);
+ }
+
+ rInfo("Point and epsilon ordering:");
+ for (unsigned i = 0; i < vertices.size(); ++i)
+ rInfo(" %4d: %4d - %f", i, vertices[i], epsilons[i]);
+
+ // Generate and sort all the edges
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ {
+ Vertex u = vertices[i];
+ Vertex v = vertices[j];
+ if (distances(u,v) <= to_multiplier*epsilons[j-1])
+ edges.push_back(std::make_pair(u,v));
+ }
+ std::sort(edges.begin(), edges.end(), RipsGenerator::ComparePair(distances));
+ rInfo("Total participating edges: %d", edges.size());
+ for (EdgeVector::const_iterator cur = edges.begin(); cur != edges.end(); ++cur)
+ rDebug(" (%d, %d) %f", cur->first, cur->second, distances(cur->first, cur->second));
+
+ // Construct zigzag
+ Complex complex;
+ Zigzag zz;
+ RipsGenerator rips(distances);
+ SimplexEvaluator size(distances);
+
+ // Insert vertices
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ {
+ // Add a vertex
+ Smplx sv; sv.add(vertices[i]);
+ rDebug("Adding %s", tostring(sv).c_str());
+ add.start();
+ complex.insert(std::make_pair(sv,
+ zz.add(Boundary(),
+ true, // vertex is always in the subcomplex
+ BirthInfo(0, 0)).first));
+ add.stop();
+ //rDebug("Newly born cycle order: %d", complex[sv]->low->order);
+ CountNum(cComplexSize, 0);
+ Count(cComplexSize);
+ Count(cOperations);
+ }
+
+ rInfo("Commencing computation");
+ boost::progress_display show_progress(vertices.size());
+ unsigned sce = 0, // index of the current one past last edge in the subcomplex
+ ce = 0; // index of the current one past last edge in the complex
+ for (unsigned stage = 0; stage != vertices.size() - 1; ++stage)
+ {
+ unsigned i = vertices.size() - 1 - stage;
+ rInfo("Current stage %d: %d %f: %f -> %f", stage,
+ vertices[i], epsilons[i-1],
+ from_multiplier*epsilons[i-1],
+ to_multiplier*epsilons[i-1]);
+
+ /* Increase epsilon */
+ // Record the cofaces of all the simplices that need to be removed and reinserted
+ SimplexSet cofaces;
+ rDebug(" Cofaces size: %d", cofaces.size());
+ while(sce < ce)
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[sce];
+ if (distances(u,v) <= from_multiplier*epsilons[i-1])
+ ++sce;
+ else
+ break;
+
+ // Skip an edge if any one of its vertices has been removed from the complex
+ bool skip_edge = false;
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ if (u == vertices[j] || v == vertices[j])
+ {
+ // Debug only: eventually remove
+ rDebug(" Skipping edge (%d, %d)", u, v);
+ Smplx s; s.add(u); s.add(v);
+ AssertMsg(complex.find(s) == complex.end(), "Simplex should not be in the complex.");
+ skip_edge = true;
+ break;
+ }
+ if (skip_edge) continue;
+ rDebug(" Generating cofaces for (%d, %d)", u, v);
+
+ ec.start();
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ to_multiplier*epsilons[i],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ ec.stop();
+ }
+ rDebug(" Recorded cofaces to remove");
+ rDebug(" Cofaces size: %d", cofaces.size());
+ // Remove all the cofaces
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing %s", tostring(*cur).c_str());
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ AssertMsg(!si->second->subcomplex, "We should not remove simplices already in the subcomplex when we increase epsilon");
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rDebug(" Removed cofaces");
+
+ // Add anything else that needs to be inserted into the complex
+ while (ce < edges.size())
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[ce];
+ if (distances(u,v) <= to_multiplier*epsilons[i-1])
+ ++ce;
+ else
+ break;
+ rDebug(" Recording cofaces of edges[%d]=(%d, %d) with size=%f", (ce-1), u, v, distances(u,v));
+ ec.start();
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ to_multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ ec.stop();
+ }
+ rDebug(" Recorded new cofaces to add");
+
+ // Progress sce
+ while (sce < ce)
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[sce];
+ rDebug(" Progressing sce=%d over (%d, %d) %f", sce, u, v, distances(u,v));
+ if (distances(u,v) <= from_multiplier*epsilons[i-1])
+ ++sce;
+ else
+ break;
+ }
+ rDebug(" Moved subcomplex index forward");
+
+ // Insert all the cofaces
+ rDebug(" Cofaces size: %d", cofaces.size());
+ for (SimplexSet::const_iterator cur = cofaces.begin(); cur != cofaces.end(); ++cur)
+ {
+ Index idx; Death d; Boundary b;
+ rDebug(" Adding %s, its size %f", tostring(*cur).c_str(), size(*cur));
+ make_boundary(*cur, complex, zz, b);
+ add.start();
+ boost::tie(idx, d) = zz.add(b,
+ size(*cur) <= from_multiplier*epsilons[i-1],
+ BirthInfo(epsilons[i-1], cur->dimension()));
+ add.stop();
+ //if (!d) rDebug("Newly born cycle order: %d", complex[*cur]->low->order);
+ CountNum(cComplexSize, cur->dimension());
+ Count(cComplexSize);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ complex.insert(std::make_pair(*cur, idx));
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rInfo("Increased epsilon; complex size: %d", complex.size());
+ show_image_betti(zz, skeleton_dimension);
+ report_memory();
+
+ /* Remove the vertex */
+ cofaces.clear();
+ rDebug(" Cofaces size: %d", cofaces.size());
+ vc.start();
+ rips.vertex_cofaces(vertices[i],
+ skeleton_dimension,
+ to_multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ vc.stop();
+ rDebug(" Computed cofaces of the vertex, their number: %d", cofaces.size());
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing: %s", tostring(*cur).c_str());
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ for (Complex::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ rDebug(" %s", tostring(cur->first).c_str());
+ show_image_betti(zz, skeleton_dimension);
+ report_memory();
+
+ ++show_progress;
+ }
+
+ // Remove the last vertex
+ AssertMsg(complex.size() == 1, "Only one vertex must remain");
+ remove.start();
+ Death d = zz.remove(complex.begin()->second, BirthInfo(epsilons[0], -1));
+ remove.stop();
+ complex.erase(complex.begin());
+ if (!d) AssertMsg(false, "The vertex must have died");
+ report_death(intervals, d, epsilons[0], epsilons[0], skeleton_dimension);
+ CountNumBy(cComplexSize, 0, -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ ++show_progress;
+
+ total.stop();
+
+ remove.check("Remove timer ");
+ add.check ("Add timer ");
+ ec.check ("Edge coface timer ");
+ vc.check ("Vertex coface timer ");
+ total.check ("Total timer ");
+
+ std::cout << "Writing intervals...";
+ // Note (hack): use epsilons[vertices.size()-2]/2 as minimal value for the log-scale intervals to avoid intervals starting at -infinity and thus a scaling effect
+ write_intervals(out, intervals, skeleton_dimension, logscale, epsilons[vertices.size()-2]/2);
+ std::cout << " done." << std::endl;
+}
+
+
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value) {
+ out << "I = { ";
+ for (int d = 0; d<skeleton_dimension; d++) {
+ out << "[ ";
+ for (std::list<std::pair<double,double> >::const_iterator pit = intervals[d].begin(); pit != intervals[d].end(); pit++)
+ if (logscale)
+ out << "[" << log2(std::max(pit->first, min_value)) << ";" << log2(std::max(pit->second, min_value)) << "] ";
+ else
+ out << "[" << pit->first << ";" << pit->second << "] ";
+
+ // add dummy interval if intervals list is empty
+ if (intervals[d].empty())
+ out << "[0;0] ";
+ out << "] ,";
+ }
+ out << "} ";
+}
+
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension)
+{
+ // std::cerr << " d = " << d;
+ // if (d)
+ // std::cerr << " d->dim = " << d->dimension
+ // << " d->dist = " << d->distance;
+ // std::cerr << " epsilon = " << epsilon << std::endl;
+
+ if (d && ((d->distance - epsilon) != 0) && (d->dimension < skeleton_dimension))
+ intervals[d->dimension].push_back(std::pair<double,double>(d->distance, birthEpsilon));
+}
+
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b)
+{
+ rDebug(" Boundary of <%s>", tostring(s).c_str());
+ for (Smplx::BoundaryIterator cur = s.boundary_begin(); cur != s.boundary_end(); ++cur)
+ {
+ b.append(c[*cur], zz.cmp);
+ rDebug(" %d (inL=%d)", c[*cur]->order, b.back()->subcomplex);
+ }
+}
+
+bool face_leaving_subcomplex(Complex::reverse_iterator si, const SimplexEvaluator& size, DistanceType after, DistanceType before)
+{
+ const Smplx& s = si->first;
+ for (Smplx::VertexContainer::const_iterator v1 = s.vertices().begin(); v1 != s.vertices().end(); ++v1)
+ for (Smplx::VertexContainer::const_iterator v2 = boost::next(v1); v2 != s.vertices().end(); ++v2)
+ {
+ Smplx e; e.add(*v1); e.add(*v2);
+ if (size(e) > after && size(e) <= before)
+ return true;
+ }
+
+ return false;
+}
+
+void show_image_betti(Zigzag& zz, Dimension skeleton)
+{
+ for (Zigzag::ZIndex cur = zz.image_begin(); cur != zz.image_end(); ++cur)
+ if (cur->low == zz.boundary_end() && cur->birth.dimension < skeleton)
+ rInfo("Class in the image of dimension: %d", cur->birth.dimension);
+}
+
+
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi)
+{ return (out << bi.distance); }
+
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& from_multiplier,
+ float& to_multiplier,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Location to save persistence pairs");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("dim,d", po::value<unsigned>(&skeleton_dimension)->default_value(3), "maximal dimension of the Rips complex")
+ ("eta,e", po::value<float>(&from_multiplier)->default_value(3), "multiplier eta used in the Rips parameter")
+ ("rho,r", po::value<float>(&to_multiplier)->default_value(3), "multiplier rho used in the Rips parameter")
+ ( "logscale,l" , po::value<bool>(&logscale)->zero_tokens(), "outputs interval bounds on log_2 scale" ) ;
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file output-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/homology-zigzags/oR-ZZ.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,502 @@
+/***************************************************************************
+
+ oR-ZZ: oscillating Rips zigzag implementation
+ Copyright (C) 2012 Steve Oudot
+
+ Adapted from the Morozov zigzag implementation provided in the
+ Rips package of the Dionysus library (see the file "M-ZZ.cpp").
+ The Dionysus library is (C) 2006-2012 Dmitriy Morozov.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+***************************************************************************/
+
+#include <topology/rips.h>
+#include <topology/zigzag-persistence.h>
+#include <utilities/types.h>
+#include <utilities/containers.h>
+
+#include <geometry/l2distance.h> // Point, PointContainer, L2DistanceType, read_points
+#include <geometry/distances.h>
+
+#include <utilities/log.h>
+#include <utilities/memory.h> // for report_memory()
+#include <utilities/timer.h>
+
+#include <map>
+#include <cmath>
+#include <fstream>
+#include <stack>
+#include <cstdlib>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#ifdef COUNTERS
+static Counter* cComplexSize = GetCounter("rips/size");
+static Counter* cOperations = GetCounter("rips/operations");
+#endif // COUNTERS
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+
+typedef PairDistances::IndexType Vertex;
+typedef Simplex<Vertex> Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef std::list<Smplx> SimplexList;
+typedef std::set<Smplx, Smplx::VertexDimensionComparison> SimplexSet;
+
+typedef std::vector<Vertex> VertexVector;
+typedef std::vector<DistanceType> EpsilonVector;
+typedef std::vector<std::pair<Vertex, Vertex> > EdgeVector;
+
+typedef Rips<PairDistances, Smplx> RipsGenerator;
+typedef RipsGenerator::Evaluator SimplexEvaluator;
+
+struct BirthInfo;
+typedef ZigzagPersistence<BirthInfo> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef std::map<Smplx, Index,
+ Smplx::VertexDimensionComparison> Complex;
+typedef Zigzag::ZColumn Boundary;
+
+typedef std::vector<std::list<std::pair<double, double> > > IntervalsVector;
+
+// Information we need to know when a class dies
+struct BirthInfo
+{
+ BirthInfo(DistanceType dist = DistanceType(), Dimension dim = Dimension()):
+ distance(dist), dimension(dim) {}
+ DistanceType distance;
+ Dimension dimension;
+};
+
+// Forward declarations of auxilliary functions
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value=0);
+// void report_death(std::ostream& out, Death d, DistanceType epsilon, Dimension skeleton_dimension);
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension);
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b);
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi);
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& eta,
+ float& rho,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename);
+
+int main(int argc, char* argv[])
+{
+
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Timer total, remove, add, ec, vc;
+ total.start();
+
+#if 0
+ SetFrequency(cOperations, 25000);
+ SetTrigger(cOperations, cComplexSize);
+#endif
+
+ unsigned skeleton_dimension;
+ float eta;
+ float rho;
+ bool logscale = false;
+ std::string infilename, outfilename;
+ process_command_line_options(argc, argv, skeleton_dimension, eta, rho, logscale, infilename, outfilename);
+
+ // Read in points
+ PointContainer points;
+ read_points(infilename, points);
+
+ std::cout << "Number of points: " << points.size() << std::endl;
+
+ // Create output file
+ std::ofstream out(outfilename.c_str());
+
+ // Create pairwise distances
+ PairDistances distances(points);
+
+ // Create intervals DS
+ IntervalsVector intervals(skeleton_dimension);
+
+ // Order vertices and epsilons (in maxmin fashion)
+ VertexVector vertices;
+ EpsilonVector epsilons;
+ EdgeVector edges;
+
+ {
+ EpsilonVector dist(distances.size(), Infinity);
+
+ vertices.push_back(distances.begin());
+ //epsilons.push_back(Infinity);
+ while (vertices.size() < distances.size())
+ {
+ for (Vertex v = distances.begin(); v != distances.end(); ++v)
+ dist[v] = std::min(dist[v], distances(v, vertices.back()));
+ EpsilonVector::const_iterator max = std::max_element(dist.begin(), dist.end());
+ vertices.push_back(max - dist.begin());
+ epsilons.push_back(*max);
+ }
+ epsilons.push_back(0);
+ }
+
+ rInfo("Point and epsilon ordering:");
+ for (unsigned i = 0; i < vertices.size(); ++i)
+ rInfo(" %4d: %4d - %f", i, vertices[i], epsilons[i]);
+
+ // Generate and sort all the edges
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ {
+ Vertex u = vertices[i];
+ Vertex v = vertices[j];
+ if (distances(u,v) <= rho*epsilons[j-1])
+ edges.push_back(std::make_pair(u,v));
+ }
+ std::sort(edges.begin(), edges.end(), RipsGenerator::ComparePair(distances));
+ std::cout << "Total participating edges: " << edges.size() << std::endl;
+ for (EdgeVector::const_iterator cur = edges.begin(); cur != edges.end(); ++cur)
+ rDebug(" (%d, %d) %f", cur->first, cur->second, distances(cur->first, cur->second));
+
+ // Construct zigzag
+ Complex complex;
+ Zigzag zz;
+ RipsGenerator rips(distances);
+ SimplexEvaluator size(distances);
+
+ // Insert vertices (initial complex is just a disjoint union of vertices)
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ {
+ // Add a vertex
+ Smplx sv; sv.add(vertices[i]);
+ rDebug("Adding %s", tostring(sv).c_str());
+ add.start();
+ complex.insert(std::make_pair(sv,
+ zz.add(Boundary(),
+ BirthInfo(0, 0)).first));
+ add.stop();
+ //rDebug("Newly born cycle order: %d", complex[sv]->low->order);
+ CountNum(cComplexSize, 0);
+ Count(cComplexSize);
+ Count(cOperations);
+ }
+
+ rInfo("Commencing computation");
+ // std::cerr << std::setprecision(15);
+ bool erased[vertices.size()];
+ for (unsigned i = 0; i<vertices.size(); i++)
+ erased[i] = false;
+ boost::progress_display show_progress(vertices.size());
+ unsigned ce = 0; // index of the current one past last edge in the complex
+ for (unsigned stage = 0; stage != vertices.size() - 1; ++stage)
+ {
+ unsigned i = vertices.size() - 1 - stage;
+
+ /* Increase epsilon */
+ // Record the cofaces of all the simplices that need to be removed and reinserted
+ SimplexSet cofaces, large_cofaces, tmp_cofaces;
+ rDebug(" Cofaces size: %d", cofaces.size());
+ // std::cerr << "Cofaces sizes: " << cofaces.size() << " "
+ // << large_cofaces.size() << " "
+ // << tmp_cofaces.size() << std::endl;
+
+ // Add anything else that needs to be inserted into the complex
+ unsigned cee = ce;
+ bool ce_set = false;
+
+ // if (stage > 0) {
+ // std::cerr << "Stage " << stage << " :";
+ // Vertex u,v;
+ // boost::tie(u,v) = edges[cee-1];
+ // std::cerr << distances(u,v) << " <= " << multiplier*epsilons[i];
+ // boost::tie(u,v) = edges[cee];
+ // std::cerr << " < " << distances(u,v) << " <= "
+ // << epsilons[i-1] << std::endl
+ // << " vertices[i] = " << vertices[i] << std::endl
+ // << " vertices[i+1] = " << vertices[i+1] << std::endl;
+ // }
+
+ // if (stage > 0)
+ // std::cerr << "Stage " << stage << " :" << std::endl;
+
+ while (cee < edges.size())
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[cee];
+ // if (stage > 0 && (u == vertices[i+1] || v == vertices[i+1]))
+ // std::cerr << "ATTENTION: [" << u << "," << v << "]" << std::endl;
+ // skip already erased edges (may appear since set of participating is larger than in the Morozov zigzag)
+
+ if (distances(u,v) > eta*epsilons[i-1] && !ce_set) {
+ ce = cee;
+ ce_set = true;
+ // std::cerr << "ce = " << distances(u,v) << " > " << eta*epsilons[i-1] << std::endl;
+ }
+ if (distances(u,v) > rho*epsilons[i-1]) {
+ // std::cerr << "cee = " << distances(u,v) << " > " << rho*epsilons[i-1] << std::endl;
+ break;
+ }
+ rDebug(" Recording cofaces of edges[%d]=(%d, %d) with size=%f", (cee-1), u, v, distances(u,v));
+ ec.start();
+ tmp_cofaces.clear();
+
+ // Ignore edges with already removed vertices
+ if (!erased[u] && !erased[v])
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ rho*epsilons[i-1],
+ make_insert_functor(tmp_cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ // insert computed cofaces to cofaces sets
+ cofaces.insert(tmp_cofaces.begin(), tmp_cofaces.end());
+ if (distances(u,v) > eta*epsilons[i-1])
+ large_cofaces.insert(tmp_cofaces.begin(), tmp_cofaces.end());
+ ec.stop();
+
+ ++cee;
+ if (cee == edges.size() && !ce_set)
+ ce = cee;
+ }
+ rDebug(" Recorded new cofaces to add");
+
+ // Insert all the cofaces
+ rDebug(" Cofaces size: %d", cofaces.size());
+ for (SimplexSet::const_iterator cur = cofaces.begin(); cur != cofaces.end(); ++cur)
+ {
+ Index idx; Death d; Boundary b;
+ rDebug(" Adding %s, its size %f", tostring(*cur).c_str(), size(*cur));
+ // std::cerr << " Adding " << *cur << ", its size " << size(*cur) << std::endl;
+ make_boundary(*cur, complex, zz, b);
+ add.start();
+ boost::tie(idx, d) = zz.add(b,
+ BirthInfo(epsilons[i-1], cur->dimension()));
+ add.stop();
+ //if (!d) rDebug("Newly born cycle order: %d", complex[*cur]->low->order);
+ CountNum(cComplexSize, cur->dimension());
+ Count(cComplexSize);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ complex.insert(std::make_pair(*cur, idx));
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rInfo("Increased epsilon to %f; complex size: %d", rho*epsilons[i-1], complex.size());
+ // std::cerr << "Increased epsilon to " << rho*epsilons[i-1] << ", complex size " << complex.size() << std::endl;
+ report_memory();
+
+
+ /* Remove edges of length between eta*epsilons[i-1] and rho*epsilons[i-1] and their cofaces */
+ for (SimplexSet::const_reverse_iterator cur = large_cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)large_cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing: %s", tostring(*cur).c_str());
+ // std::cerr << " Removing " << *cur << std::endl;
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rInfo("Decreased epsilon to %f; complex size: %d", eta*epsilons[i-1], complex.size());
+ for (Complex::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ rDebug(" %s", tostring(cur->first).c_str());
+ report_memory();
+ // std::cerr << "Decreased epsilon to " << eta*epsilons[i-1] << ", complex size " << complex.size() << std::endl;
+
+
+ /* Remove the vertex */
+ cofaces.clear();
+ rDebug(" Cofaces size: %d", cofaces.size());
+ vc.start();
+ rips.vertex_cofaces(vertices[i],
+ skeleton_dimension,
+ eta*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ vc.stop();
+ rDebug(" Computed cofaces of the vertex, their number: %d", cofaces.size());
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing: %s", tostring(*cur).c_str());
+ // std::cerr << " Removing " << *cur << std::endl;
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(intervals, d, epsilons[i-1], epsilons[i], skeleton_dimension);
+ }
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ for (Complex::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ rDebug(" %s", tostring(cur->first).c_str());
+ // std::cerr << " Removed vertex " << vertices[i] << ", complex size " << complex.size() << std::endl;
+
+ // Book-keep set of erased vertices
+ erased[vertices[i]] = true;
+
+ report_memory();
+
+ ++show_progress;
+ }
+
+ // Remove the last vertex
+ AssertMsg(complex.size() == 1, "Only one vertex must remain");
+ remove.start();
+ Death d = zz.remove(complex.begin()->second, BirthInfo(epsilons[0], -1));
+ remove.stop();
+ complex.erase(complex.begin());
+ if (!d) AssertMsg(false, "The vertex must have died");
+ report_death(intervals, d, epsilons[0], epsilons[0], skeleton_dimension);
+ CountNumBy(cComplexSize, 0, -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ ++show_progress;
+
+ total.stop();
+
+ remove.check("Remove timer ");
+ add.check ("Add timer ");
+ ec.check ("Edge coface timer ");
+ vc.check ("Vertex coface timer ");
+ total.check ("Total timer ");
+
+ std::cout << "Writing intervals...";
+ // Note (hack): use epsilons[vertices.size()-2]/2 as minimal value for the log-scale intervals to avoid intervals starting at -infinity and thus a scaling effect
+ write_intervals(out, intervals, skeleton_dimension, logscale, epsilons[vertices.size()-2]/2);
+ std::cout << " done." << std::endl;
+}
+
+
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension, bool logscale, double min_value) {
+ out << "I = { ";
+ for (int d = 0; d<skeleton_dimension; d++) {
+ out << "[ ";
+ for (std::list<std::pair<double,double> >::const_iterator pit = intervals[d].begin(); pit != intervals[d].end(); pit++)
+ if (logscale)
+ out << "[" << log2(std::max(pit->first, min_value)) << ";" << log2(std::max(pit->second, min_value)) << "] ";
+ else
+ out << "[" << pit->first << ";" << pit->second << "] ";
+
+ // add dummy interval if intervals list is empty
+ if (intervals[d].empty())
+ out << "[0;0] ";
+ out << "] ,";
+ }
+ out << "} ";
+}
+
+void report_death(IntervalsVector& intervals, Death d, DistanceType epsilon, DistanceType birthEpsilon, Dimension skeleton_dimension)
+{
+ // std::cerr << " d = " << d;
+ // if (d)
+ // std::cerr << " d->dim = " << d->dimension
+ // << " d->dist = " << d->distance;
+ // std::cerr << " epsilon = " << epsilon << std::endl;
+
+ if (d && ((d->distance - epsilon) != 0) && (d->dimension < skeleton_dimension))
+ intervals[d->dimension].push_back(std::pair<double,double>(d->distance, birthEpsilon));
+}
+
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b)
+{
+ rDebug(" Boundary of <%s>", tostring(s).c_str());
+ for (Smplx::BoundaryIterator cur = s.boundary_begin(); cur != s.boundary_end(); ++cur)
+ {
+ b.append(c[*cur], zz.cmp);
+ rDebug(" %d", c[*cur]->order);
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi)
+{ return (out << bi.distance); }
+
+
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& eta,
+ float& rho,
+ bool& logscale,
+ std::string& infilename,
+ std::string& outfilename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Location to save persistence pairs");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("dim,d", po::value<unsigned>(&skeleton_dimension)->default_value(3), "maximal dimension of the Rips complex")
+ ("eta,e", po::value<float>(&eta)->default_value(3), "multiplier eta used in the Rips parameter")
+ ("rho,r", po::value<float>(&rho)->default_value(3), "multiplier rho used in the Rips parameter")
+ ( "logscale,l" , po::value<bool>(&logscale)->zero_tokens(), "outputs interval bounds on log_2 scale" ) ;
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file output-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/homology-zigzags/rips-pairwise.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,222 @@
+/***************************************************************************
+
+ rips-pairwise: Rips filtration + persistent homology
+ Copyright (C) 2009-2012 Dmitriy Morozov
+
+ Adapted from its original version ("rips-pairwise.cpp" in
+ the Dionysus library) by Steve Oudot (2012).
+
+ Changelog (2012-11-22):
+
+ - the barcode is now output on a log scale and in a format that is
+ compatible with the Matlab layer of the PLEX 2.5 library,
+
+ - the barcode representation is now by closed intervals instead of
+ half-open intervals.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+***************************************************************************/
+
+#include <topology/rips.h>
+#include <topology/filtration.h>
+#include <topology/static-persistence.h>
+#include <topology/dynamic-persistence.h>
+#include <topology/persistence-diagram.h>
+
+#include <geometry/l2distance.h>
+#include <geometry/distances.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+#include <utilities/timer.h>
+
+#include <vector>
+
+#include <boost/program_options.hpp>
+
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+typedef PairDistances::IndexType Vertex;
+
+typedef Rips<PairDistances> Generator;
+typedef Generator::Simplex Smplx;
+typedef Filtration<Smplx> Fltr;
+// typedef StaticPersistence<> Persistence;
+typedef DynamicPersistenceChains<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+
+typedef std::vector<std::list<std::pair<double, double> > > IntervalsVector;
+
+// Forward declarations of auxilliary functions
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension);
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, std::string& diagram_name);
+
+int main(int argc, char* argv[])
+{
+ Dimension skeleton;
+ DistanceType max_distance;
+ std::string infilename, diagram_name;
+
+ program_options(argc, argv, infilename, skeleton, max_distance, diagram_name);
+ std::ofstream diagram_out(diagram_name.c_str());
+ std::cout << "Diagram: " << diagram_name << std::endl;
+
+ PointContainer points;
+ read_points(infilename, points);
+
+ PairDistances distances(points);
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ Fltr f;
+
+ // Generate 2-skeleton of the Rips complex for epsilon = 50
+ rips.generate(skeleton, max_distance, make_push_back_functor(f));
+ std::cout << "# Generated complex of size: " << f.size() << std::endl;
+
+ // Generate filtration with respect to distance and compute its persistence
+ f.sort(Generator::Comparison(distances));
+
+ Timer persistence_timer; persistence_timer.start();
+ Persistence p(f);
+ p.pair_simplices();
+ persistence_timer.stop();
+
+#if 1
+ // Create intervals DS
+ IntervalsVector intervals(skeleton);
+
+ // Output cycles
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(f);
+ for (Persistence::iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ const Persistence::Cycle& cycle = cur->cycle;
+
+ if (!cur->sign()) // only negative simplices have non-empty cycles
+ {
+ Persistence::OrderIndex birth = cur->pair; // the cycle that cur killed was born when we added birth (another simplex)
+
+ const Smplx& b = m[birth];
+ const Smplx& d = m[cur];
+
+ // if (b.dimension() != 1) continue;
+ // std::cout << "Pair: (" << size(b) << ", " << size(d) << ")" << std::endl;
+ if (b.dimension() >= skeleton) continue;
+ // diagram_out << b.dimension() << " " << size(b) << " " << size(d) << std::endl;
+ intervals[b.dimension()].push_back(std::pair<double,double>(size(b), size(d)));
+ } else if (cur->unpaired()) // positive could be unpaired
+ {
+ const Smplx& b = m[cur];
+ // if (b.dimension() != 1) continue;
+
+ // std::cout << "Unpaired birth: " << size(b) << std::endl;
+ // cycle = cur->chain; // TODO
+ if (b.dimension() >= skeleton) continue;
+ // diagram_out << b.dimension() << " " << size(b) << " inf" << std::endl;
+ intervals[b.dimension()].push_back(std::pair<double,double>(size(b), -1));
+ }
+
+ // Iterate over the cycle
+ // for (Persistence::Cycle::const_iterator si = cycle.begin();
+ // si != cycle.end(); ++si)
+ // {
+ // const Smplx& s = m[*si];
+ // //std::cout << s.dimension() << std::endl;
+ // const Smplx::VertexContainer& vertices = s.vertices(); // std::vector<Vertex> where Vertex = Distances::IndexType
+ // AssertMsg(vertices.size() == s.dimension() + 1, "dimension of a simplex is one less than the number of its vertices");
+ // std::cout << vertices[0] << " " << vertices[1] << std::endl;
+ // }
+ }
+#endif
+
+ persistence_timer.check("# Persistence timer");
+
+ std::cout << "Writing intervals...";
+ // Note (hack): use epsilons[vertices.size()-2]/2 as minimal value for the log-scale intervals to avoid intervals starting at -infinity and thus a scaling effect
+ write_intervals(diagram_out, intervals, skeleton);
+ std::cout << " done." << std::endl;
+
+}
+
+
+const double LOG2 = std::log(2.0);
+double log2(double x) {
+ return std::log(x) / LOG2;
+}
+
+void write_intervals(std::ostream& out, const IntervalsVector& intervals, int skeleton_dimension) {
+ out << "I = { ";
+ for (int d = 0; d<skeleton_dimension; d++) {
+ out << "[ ";
+ for (std::list<std::pair<double,double> >::const_iterator pit = intervals[d].begin(); pit != intervals[d].end(); pit++) {
+ if (pit->first == 0)
+ out << "[-Inf;";
+ else
+ out << "[" << log2(pit->first) << ";";
+ if (pit->second >= 0)
+ out << log2(pit->second) << "] ";
+ else
+ out << "Inf] ";
+ }
+ // add dummy interval if intervals list is empty
+ if (intervals[d].empty())
+ out << "[0;0] ";
+ out << "] ,";
+ }
+ out << "} " << std::endl;
+}
+
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, std::string& diagram_name)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<Dimension>(&skeleton)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("max-distance,m", po::value<DistanceType>(&max_distance)->default_value(Infinity), "Maximum value for the Rips complex construction")
+ ("diagram,d", po::value<std::string>(&diagram_name), "Filename where to output the persistence diagram");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,16 @@
+set (targets
+ test-grid2D
+ test-grid2D-vineyard
+ combustion-vineyard
+ pl-vineyard)
+
+if (use_dsrpdb)
+ set (t pdbdistance-vineyard)
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${dsrpdb_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+endif (use_dsrpdb)
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/combustion-vineyard.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,88 @@
+/*
+ * Author: Dmitriy Morozov
+ * Department of Computer Science, Duke University, 2005
+ */
+
+#include <utilities/log.h>
+
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+
+#include "grid2D.h"
+#include <topology/lsvineyard.h>
+
+const int xsize = 600;
+const int ysize = 600;
+const int var = 0; // which variable to use out of nc of them in each record in the file
+
+template<typename T>
+void read(std::ifstream& ifs, T& var)
+{
+ ifs.read(reinterpret_cast<char*>(&var), sizeof(T));
+}
+
+int main(int argc, char** argv)
+{
+ if (argc < 3)
+ {
+ std::cout << "Usage: combustion-vineyard FRAME1 FRAME2" << std::endl;
+ exit(0);
+ }
+
+ int size0, nc0;
+ int size1, nc1;
+
+ std::cout << "Reading: " << argv[1] << std::endl;
+ std::ifstream ifs0(argv[1], std::ios::binary);
+ std::cout << "Reading: " << argv[2] << std::endl;
+ std::ifstream ifs1(argv[2], std::ios::binary);
+
+ if (!ifs0 || !ifs1)
+ {
+ std::cout << "Could not open the frames" << std::endl;
+ exit(0);
+ }
+
+ read(ifs0, size0); read(ifs0, nc0);
+ read(ifs1, size1); read(ifs1, nc1);
+
+ assert(size0 == size1); assert(nc0 == nc1);
+ assert(size0 == xsize*ysize);
+
+ Grid2D g0(xsize, ysize), g1(xsize, ysize);
+
+ for (int y = 0; y < ysize; ++y)
+ for (int x = 0; x < xsize; ++x)
+ for (int d = 0; d < nc0; ++d)
+ {
+ float val0, val1;
+ read(ifs0, val0);
+ read(ifs1, val1);
+ if (d == var)
+ {
+ g0(x,y) = val0;
+ g1(x,y) = val1;
+ }
+ }
+ std::cout << "Grids read" << std::endl;
+
+ // Generate the complex, initialize the vineyard (which also computes the pairing)
+ typedef LSVineyard<Grid2D::CoordinateIndex, Grid2D> Grid2DVineyard;
+
+ Grid2DVineyard::LSFiltration simplices;
+ g0.complex_generator(make_push_back_functor(simplices));
+ Grid2DVineyard::VertexComparison vcmp(g0);
+ Grid2DVineyard::SimplexComparison scmp(vcmp);
+ simplices.sort(scmp);
+
+ Grid2DVineyard v(g0.begin(), g0.end(), simplices, g0);
+ std::cout << "Filtration generated, size: " << v.filtration().size() << std::endl;
+ std::cout << "Pairing computed" << std::endl;
+
+ // Compute vineyard
+ v.compute_vineyard(g1);
+ std::cout << "Vineyard computed" << std::endl;
+
+ v.vineyard().save_edges("combustion");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/cube.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,40 @@
+def log2(x):
+ i = 0
+ while x:
+ x >>= 1
+ i += 1
+ return i-1
+
+class Cube:
+ def __init__(self, vertices):
+ self.vertices = vertices
+
+ def dimension(self):
+ return log2(len(self.vertices))
+
+ def boundary(self):
+ for i in xrange(self.dimension()):
+ for side in [0,1]:
+ vertices = []
+ for idx in xrange(len(self.vertices)/2):
+ # Insert i-th bit equal to side
+ v = (idx & ~(2**i-1)) << 1
+ if side: v |= 2**i
+ v |= (idx & (2**i - 1))
+ vertices.append(self.vertices[v])
+ yield Cube(vertices)
+
+ def __hash__(self):
+ return hash(tuple(self.vertices))
+
+ def __eq__(self, other):
+ return self.vertices == other.vertices
+
+ def __repr__(self):
+ return " ".join(map(str, self.vertices))
+
+
+if __name__ == '__main__':
+ c = Cube(['a', 'b', 'c', 'd'])
+ print c
+ for sb in c.boundary(): print sb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/grid2D.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,96 @@
+/*
+ * Author: Dmitriy Morozov
+ * Department of Computer Science, Duke University, 2005 -- 2006
+ */
+
+#ifndef __GRID2D_H__
+#define __GRID2D_H__
+
+#include <memory>
+#include <vector>
+#include <map>
+#include <set>
+#include <limits>
+#include <iostream>
+//#include <cmath>
+
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/base_object.hpp>
+#include <boost/serialization/split_member.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/export.hpp>
+
+#include <boost/iterator/counting_iterator.hpp>
+
+#include "utilities/types.h"
+#include "topology/simplex.h"
+
+/**
+ * Grid2D stores a grid
+ */
+class Grid2D
+{
+ public:
+ typedef RealType ValueType;
+ typedef ValueType result_type;
+
+ typedef unsigned int CoordinateIndex;
+ typedef boost::counting_iterator<CoordinateIndex> iterator;
+
+ typedef Simplex<CoordinateIndex> Smplx;
+ typedef std::vector<ValueType> ValueVector;
+
+ public:
+ Grid2D(CoordinateIndex xx = 1, CoordinateIndex yy = 1);
+
+ /// Sets the grid dimensions to (xx,yy)
+ void change_dimensions(CoordinateIndex xx, CoordinateIndex yy);
+
+ ValueType& operator()(CoordinateIndex i, CoordinateIndex j) { return data[i*x + j]; }
+ const ValueType& operator()(CoordinateIndex i, CoordinateIndex j) const { return data[i*x + j]; }
+ ValueType& operator()(CoordinateIndex i) { return data[i]; }
+ const ValueType& operator()(CoordinateIndex i) const { return data[i]; }
+
+ CoordinateIndex xsize() const { return x; }
+ CoordinateIndex ysize() const { return y; }
+ CoordinateIndex size() const { return x*y; }
+
+ iterator begin() const { return iterator(0); }
+ iterator end() const { return iterator(size()); }
+
+ /* Given a sequential index of an element return its coordinates */
+ CoordinateIndex xpos(CoordinateIndex i) const { return i / x; }
+ CoordinateIndex ypos(CoordinateIndex i) const { return i % x; }
+ CoordinateIndex seq(CoordinateIndex i, CoordinateIndex j) const;
+
+ template<class Functor>
+ void complex_generator(const Functor& f);
+
+ std::ostream& operator<<(std::ostream& out) const;
+
+ static const CoordinateIndex INVALID_INDEX = -1;
+
+ private:
+ CoordinateIndex x,y;
+ ValueVector data;
+
+#if 0
+ private:
+ // Serialization
+ friend class boost::serialization::access;
+
+ template<class Archive> void save(Archive& ar, version_type ) const;
+ template<class Archive> void load(Archive& ar, version_type );
+
+ BOOST_SERIALIZATION_SPLIT_MEMBER()
+#endif
+};
+//BOOST_CLASS_EXPORT(Grid2D)
+
+
+std::ostream& operator<<(std::ostream& out, const Grid2D& grid) { return grid.operator<<(out); }
+
+
+#include "grid2D.hpp"
+
+#endif // __GRID2D_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/grid2D.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,121 @@
+#include <iostream>
+#include <limits>
+
+/* Implementations */
+
+Grid2D::
+Grid2D(CoordinateIndex xx, CoordinateIndex yy):
+ x(xx), y(yy), data(x*y)
+{}
+
+void
+Grid2D::
+change_dimensions(CoordinateIndex xx, CoordinateIndex yy)
+{
+ x = xx; y = yy;
+ data.resize(x*y);
+}
+
+Grid2D::CoordinateIndex
+Grid2D::
+seq(CoordinateIndex i, CoordinateIndex j) const
+{
+ // Do not forget to check if less than 0, if Index is made signed --- dangerous
+ if (i >= x || j >= y)
+ return INVALID_INDEX;
+
+ return i*x + j;
+}
+
+std::ostream&
+Grid2D::
+operator<<(std::ostream& out) const
+{
+ for (Grid2D::CoordinateIndex i = 0; i < xsize(); ++i)
+ {
+ for (Grid2D::CoordinateIndex j = 0; j < ysize(); ++j)
+ std::cout << operator()(i, j) << ' ';
+ std::cout << std::endl;
+ }
+ return out;
+}
+
+#if 0
+using boost::serialization::make_nvp;
+
+template<class Archive>
+void
+Grid2D::
+save(Archive& ar, version_type ) const
+{
+ ar << BOOST_SERIALIZATION_NVP(x);
+ ar << BOOST_SERIALIZATION_NVP(y);
+ ar << make_nvp("data", data);
+}
+
+template<class Archive>
+void
+Grid2D::
+load(Archive& ar, version_type )
+{
+ ar >> make_nvp("x", x);
+ ar >> make_nvp("y", y);
+ ar >> make_nvp("data", data);
+}
+#endif
+
+template<class Functor>
+void
+Grid2D::
+complex_generator(const Functor& f)
+{
+ for (CoordinateIndex x = 0; x < xsize() - 1; ++x)
+ for (CoordinateIndex y = 0; y < ysize() - 1; ++y)
+ {
+ CoordinateIndex v(seq(x,y));
+ CoordinateIndex vh(seq(x+1,y));
+ CoordinateIndex vv(seq(x,y+1));
+ CoordinateIndex vd(seq(x+1,y+1));
+
+ Smplx sh; sh.add(v);
+ f(sh);
+ sh.add(vh); f(sh); // Horizontal edge
+ sh.add(vd); f(sh); // "Horizontal" triangle
+
+ Smplx sv; sv.add(v);
+ sv.add(vv); f(sv); // Vertical edge
+ sv.add(vd); f(sv); // "Vertical" triangle
+
+ Smplx sd; sd.add(v);
+ sd.add(vd); f(sd); // Diagonal edge
+
+ if (y == ysize() - 2)
+ {
+ Smplx s; s.add(vv);
+ s.add(vd); f(s); // Top edge
+ }
+ if (x == xsize() - 2)
+ {
+ Smplx s; s.add(vh);
+ s.add(vd); f(s); // Right edge
+ }
+ }
+
+ // Last row
+ for (CoordinateIndex x = 0; x < xsize(); ++x)
+ {
+ std::cout << x << " " << ysize() - 1 << " " << seq(x, ysize() - 1) << std::endl;
+ CoordinateIndex v(seq(x, ysize() - 1));
+ Smplx s; s.add(v);
+ f(s);
+ }
+
+ // Last column
+ for (CoordinateIndex y = 0; y < ysize() - 1; ++y)
+ {
+ std::cout << xsize() - 1 << " " << y << " " << seq(xsize() - 1, y) << std::endl;
+ CoordinateIndex v(seq(xsize() - 1, y));
+ Smplx s; s.add(v);
+ f(s);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/lscubes.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+from dionysus import CohomologyPersistence
+from cube import Cube
+from sys import argv, exit
+
+
+def max_vertex(s, vertices):
+ return max((vertices[v] for v in s.vertices))
+
+def max_vertex_cmp(s1, s2, vertices):
+ m1 = max_vertex(s1, vertices)
+ m2 = max_vertex(s2, vertices)
+ return cmp(m1, m2) or cmp(s1.dimension(), s2.dimension())
+
+def lsf(values_filename, cubes_filename, prime = 11):
+ # Read vertices
+ vertices = []
+ with open(values_filename) as f:
+ for line in f:
+ if line.startswith('#'): continue
+ vertices.append(float(line.split()[0]))
+
+ # Read cubes
+ fltr = []
+ with open(cubes_filename) as f:
+ for line in f:
+ if line.startswith('#'): continue
+ fltr.append(Cube(map(int, line.split())))
+ fltr.sort(lambda x,y: max_vertex_cmp(x,y,vertices))
+ for i,c in enumerate(fltr): c.data = i
+
+ ch = CohomologyPersistence(prime)
+ complex = {}
+
+ for c in fltr:
+ # print "%s: %s" % (c, " + ".join(map(str, c.boundary())))
+ # print complex
+ i,d,_ = ch.add([complex[cb] for cb in c.boundary()], c.data)
+ complex[c] = i
+ if d:
+ birth = d
+ print c.dimension() - 1, max_vertex(fltr[birth], vertices), max_vertex(c, vertices)
+
+ for ccl in ch:
+ birth = ccl.birth
+ c = fltr[birth]
+ print c.dimension(), max_vertex(c, vertices), 'inf'
+
+if __name__ == '__main__':
+ if len(argv) < 3:
+ print "Usage: %s VERTICES CUBES" % argv[0]
+ print
+ print "Computes persistence of the lower star filtration of the cubical "
+ print "complex explicitly listed out in CUBES with vertex values given in VERTICES."
+ exit()
+
+ values = argv[1]
+ cubes = argv[2]
+
+ lsf(values, cubes)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/lsfiltration.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+from dionysus import Simplex, Filtration, StaticPersistence, vertex_cmp
+from sys import argv, exit
+
+
+def max_vertex(s, vertices):
+ return max((vertices[v] for v in s.vertices))
+
+def max_vertex_cmp(s1, s2, vertices):
+ m1 = max_vertex(s1, vertices)
+ m2 = max_vertex(s2, vertices)
+ return cmp(m1, m2) or cmp(s1.dimension(), s2.dimension())
+
+def lsf(values_filename, simplices_filename):
+ # Read vertices
+ vertices = []
+ with open(values_filename) as f:
+ for line in f:
+ if line.startswith('#'): continue
+ vertices.append(float(line.split()[0]))
+
+ # Read simplices
+ fltr = Filtration()
+ with open(simplices_filename) as f:
+ for line in f:
+ if line.startswith('#'): continue
+ fltr.append(Simplex(map(int, line.split())))
+ fltr.sort(lambda x,y: max_vertex_cmp(x,y,vertices))
+
+ # Compute persistence
+ p = StaticPersistence(fltr)
+ p.pair_simplices()
+
+ # Output the persistence diagram
+ smap = p.make_simplex_map(fltr)
+ for i in p:
+ if not i.sign(): continue
+
+ b = smap[i]
+ d = smap[i.pair()]
+
+ if i.unpaired():
+ print b.dimension(), max_vertex(b, vertices), "inf"
+ continue
+
+ print b.dimension(), max_vertex(b, vertices), max_vertex(d, vertices)
+
+
+if __name__ == '__main__':
+ if len(argv) < 3:
+ print "Usage: %s VERTICES SIMPLICES" % argv[0]
+ print
+ print "Computes persistence of the lower star filtration of the simplicial "
+ print "complex explicitly listed out in SIMPLICES with vertex values given in VERTICES."
+ exit()
+
+ values = argv[1]
+ simplices = argv[2]
+
+ lsf(values, simplices)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/pdbdistance-vineyard.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,124 @@
+//#include <boost/archive/binary_oarchive.hpp>
+#include "utilities/log.h"
+
+#include "pdbdistance.h"
+#include <topology/lsvineyard.h>
+
+#include <fstream>
+#include <string>
+#include <sstream>
+
+#include <boost/program_options.hpp>
+
+void program_options(int argc, char* argv[], std::string& input_fn_list,
+ std::string& output_prefix,
+ bool& all_atoms,
+ bool& save_vines);
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+#endif
+
+ std::string input_fn_list, output_fn;
+ bool all_atoms = false, save_vines = false;
+ program_options(argc, argv, input_fn_list, output_fn, all_atoms, save_vines);
+
+ // Compute initial filtration
+ std::vector<std::string> frame_fns;
+ std::ifstream in_fns(input_fn_list.c_str());
+ std::string fn;
+ while(std::getline(in_fns, fn))
+ frame_fns.push_back(fn);
+
+ std::ifstream in(frame_fns[0].c_str());
+ PDBDistanceGrid ginit(in, !all_atoms);
+ in.close();
+
+ typedef LSVineyard<Grid2D::CoordinateIndex, Grid2D> Grid2DVineyard;
+
+ Grid2DVineyard::LSFiltration simplices;
+ ginit.complex_generator(make_push_back_functor(simplices));
+ Grid2DVineyard::VertexComparison vcmp(ginit);
+ Grid2DVineyard::SimplexComparison scmp(vcmp);
+ simplices.sort(scmp);
+ std::cout << "Complex generated, size: " << simplices.size() << std::endl;
+
+ Grid2DVineyard v(ginit.begin(), ginit.end(), simplices, ginit);
+ std::cout << "Filtration generated, size: " << v.filtration().size() << std::endl;
+ std::cout << "Pairing computed" << std::endl;
+
+ // Process frames computing the vineyard
+ for (size_t i = 1; i < frame_fns.size(); ++i)
+ {
+ std::cout << "Processing " << frame_fns[i] << std::endl;
+ in.open(frame_fns[i].c_str());
+ v.compute_vineyard(PDBDistanceGrid(in, !all_atoms));
+ in.close();
+ }
+ std::cout << "Vineyard computed" << std::endl;
+
+ if (save_vines)
+ v.vineyard().save_vines(output_fn);
+ else
+ v.vineyard().save_edges(output_fn);
+
+#if 0
+ std::ofstream ofs(output_fn.c_str(), std::ios::binary);
+ boost::archive::binary_oarchive oa(ofs);
+ oa << make_nvp("Filtration", pgf);
+ ofs.close();
+#endif
+}
+
+void program_options(int argc, char* argv[], std::string& input_fn_list,
+ std::string& output_prefix,
+ bool& all_atoms,
+ bool& save_vines)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-fn-list", po::value<std::string>(&input_fn_list), "prefix of the input frames")
+ ("output-prefix", po::value<std::string>(&output_prefix), "output prefix");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("all-atoms,a", po::bool_switch(&all_atoms), "process all atoms (not only alpha carbons)")
+ ("save-vines,v", po::bool_switch(&save_vines), "save vines instead of edges")
+ ("help,h", "produce help message");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-fn-list", 1);
+ pos.add("output-prefix", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("input-fn-list") || !vm.count("output-prefix"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-fn-list output-prefix" << std::endl;
+ std::cout << visible << std::endl;
+ std::cout << std::endl;
+ std::cout << "Computes a vineyard of the pairwise distance function for a sequence of PDB frames." << std::endl;
+ std::cout << "Frames are listed in input-fn-list file." << std::endl;
+ std::abort();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/pdbdistance.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,87 @@
+/*
+ * Author: Dmitriy Morozov
+ * Department of Computer Science, Duke University, 2005 -- 2006
+ *
+ * Depends on Daniel Russel's "simple C++ PDB" (aka DSR-PDB).
+ */
+
+#ifndef __PDBDISTANCE_H__
+#define __PDBDISTANCE_H__
+
+#include <fstream>
+#include <string>
+#include <dsrpdb/Protein.h>
+#include <dsrpdb/iterator.h>
+#include <cmath>
+
+#include <boost/serialization/access.hpp>
+
+#include "utilities/types.h"
+#include "grid2D.h"
+
+#include <boost/serialization/export.hpp>
+
+
+class PDBDistanceGrid: public Grid2D
+{
+ public:
+ PDBDistanceGrid()
+ {}
+
+ PDBDistanceGrid(std::istream& in, bool ca_only = true)
+ {
+ load_stream(in, ca_only);
+ }
+
+ void load_stream(std::istream& in, bool ca_only = true)
+ {
+ dsrpdb::Protein p(in);
+ typedef std::vector<dsrpdb::Point> PointVector;
+ PointVector coordinates;
+ if (ca_only)
+ {
+ PointVector v(ca_coordinates_begin(p), ca_coordinates_end(p));
+ coordinates.swap(v);
+ }
+ else
+ {
+ PointVector v(backbone_coordinates_begin(p), backbone_coordinates_end(p));
+ coordinates.swap(v);
+ }
+
+ std::cout << "Coordinatess created, size: " << coordinates.size() << std::endl;
+
+ Grid2D::change_dimensions(coordinates.size(), coordinates.size());
+ for (Grid2D::CoordinateIndex i = 0; i < coordinates.size(); ++i)
+ for (Grid2D::CoordinateIndex j = 0; j < coordinates.size(); ++j)
+ {
+ if (i < j)
+ Grid2D::operator()(i,j) = distance(coordinates[i], coordinates[j]);
+ else
+ Grid2D::operator()(i,j) = 0;
+ }
+ }
+
+ private:
+ Grid2D::ValueType distance(dsrpdb::Point p1, dsrpdb::Point p2) const
+ {
+ dsrpdb::Vector v = p1 - p2;
+ return std::sqrt(v*v);
+ }
+
+#if 0
+ private:
+ // Serialization
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, version_type version)
+ {
+ ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Grid2D);
+ }
+#endif
+};
+
+//BOOST_CLASS_EXPORT(PDBDistanceGrid)
+
+#endif // __PDBDISTANCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/pl-vineyard.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,169 @@
+#include <utilities/log.h>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <topology/lsvineyard.h>
+
+#include <boost/program_options.hpp>
+#include <boost/iterator/counting_iterator.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+namespace bl = boost::lambda;
+
+
+typedef double VertexValue;
+typedef unsigned Vertex;
+typedef std::vector<VertexValue> VertexVector;
+struct SubscriptFunctor: public std::unary_function<Vertex, VertexValue>
+{
+ SubscriptFunctor(const VertexVector& v): vec(&v) {}
+ float operator()(Vertex i) const { return (*vec)[i]; }
+ SubscriptFunctor& operator=(const SubscriptFunctor& other) { vec = other.vec; return *this; }
+ const VertexVector* vec;
+};
+typedef SubscriptFunctor VertexEvaluator;
+typedef std::vector<VertexVector> VertexVectorVector;
+typedef LSVineyard<Vertex, VertexEvaluator> PLVineyard;
+typedef PLVineyard::Simplex Smplx; // gotta start using namespaces
+
+void program_options(int argc, char* argv[], std::string& complex_fn,
+ std::string& values_fn,
+ std::string& output_prefix,
+ bool& skip_infinite_vines,
+ bool& save_vines,
+ bool& explicit_events);
+void read_simplices(const std::string& complex_fn, PLVineyard::LSFiltration& simplices);
+void read_vertices(const std::string& vertex_fn, VertexVectorVector& vertices);
+
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+#endif
+
+ std::string complex_fn, values_fn, output_prefix;
+ bool skip_infinite_vines = false, explicit_events = false, save_vines = false;
+ program_options(argc, argv, complex_fn, values_fn, output_prefix, skip_infinite_vines, save_vines, explicit_events);
+
+
+ // Read in the complex
+ PLVineyard::LSFiltration simplices;
+ read_simplices(complex_fn, simplices);
+ std::cout << "Complex read, size: " << simplices.size() << std::endl;
+
+ // Read in vertex values
+ VertexVectorVector vertices;
+ read_vertices(values_fn, vertices);
+
+ // Setup the vineyard
+ VertexEvaluator veval(vertices[0]);
+ PLVineyard::VertexComparison vcmp(veval);
+ PLVineyard::SimplexComparison scmp(vcmp);
+ simplices.sort(scmp);
+ PLVineyard v(boost::counting_iterator<Vertex>(0),
+ boost::counting_iterator<Vertex>(vertices[0].size()),
+ simplices, veval);
+ std::cout << "Pairing computed" << std::endl;
+
+ // Compute vineyard
+ for (size_t i = 1; i < vertices.size(); ++i)
+ {
+ veval = VertexEvaluator(vertices[i]);
+ v.compute_vineyard(veval);
+ std::cout << "Processed frame: " << i << std::endl;
+ }
+ std::cout << "Vineyard computed" << std::endl;
+
+ if (save_vines)
+ v.vineyard().save_vines(output_prefix, skip_infinite_vines);
+ else
+ v.vineyard().save_edges(output_prefix, skip_infinite_vines);
+}
+
+
+void read_simplices(const std::string& complex_fn, PLVineyard::LSFiltration& simplices)
+{
+ std::ifstream in(complex_fn.c_str());
+ std::string line;
+ while (std::getline(in, line))
+ {
+ std::istringstream strin(line);
+ simplices.push_back(Smplx(std::istream_iterator<Vertex>(strin), std::istream_iterator<Vertex>()));
+ }
+ std::cout << "Simplices read:" << std::endl;
+ std::copy(simplices.begin(), simplices.end(), std::ostream_iterator<Smplx>(std::cout, "\n"));
+}
+
+void read_vertices(const std::string& vertex_fn, VertexVectorVector& vertices)
+{
+ std::ifstream in(vertex_fn.c_str());
+ std::string line;
+ while (std::getline(in, line))
+ {
+ std::istringstream strin(line);
+ vertices.push_back(VertexVector(std::istream_iterator<VertexValue>(strin), std::istream_iterator<VertexValue>()));
+ }
+ std::cout << "Vertex values read:" << std::endl;
+ for (size_t i = 0; i < vertices.size(); ++i)
+ {
+ std::copy(vertices[i].begin(), vertices[i].end(), std::ostream_iterator<VertexValue>(std::cout, " "));
+ std::cout << std::endl;
+ }
+}
+
+void program_options(int argc, char* argv[], std::string& complex_fn,
+ std::string& values_fn,
+ std::string& output_prefix,
+ bool& skip_infinite_vines,
+ bool& save_vines,
+ bool& explicit_events)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("complex-file", po::value<std::string>(&complex_fn), "file listing the simplices of the complex")
+ ("values-file", po::value<std::string>(&values_fn), "file listing the values at the vertices")
+ ("output-prefix", po::value<std::string>(&output_prefix), "output prefix");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("skip-infinite,s", po::bool_switch(&skip_infinite_vines), "skip infinite vines in the output")
+ ("explicit-events,e", po::bool_switch(&explicit_events), "process kinetic sort events one by one")
+ ("save-vines,v", po::bool_switch(&save_vines), "save vines instead of edges")
+ ("help,h", "produce help message");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("complex-file", 1);
+ pos.add("values-file", 1);
+ pos.add("output-prefix", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("complex-file") || !vm.count("values-file") || !vm.count("output-prefix"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] complex-file values-file output-prefix" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/test-grid2D-vineyard.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,54 @@
+#include <utilities/log.h>
+
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+
+#include "grid2D.h"
+#include <topology/lsvineyard.h>
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+ // stdoutLog.subscribeTo(RLOG_CHANNEL("topology"));
+ // stdoutLog.subscribeTo(RLOG_CHANNEL("topology/persistence/transpositions"));
+ // stdoutLog.subscribeTo(RLOG_CHANNEL("topology/vineyard"));
+ stdoutLog.subscribeTo(RLOG_CHANNEL("lsvineyard"));
+#endif
+
+ Grid2D g0(2, 2), g1(2, 2);
+ g0(0,0) = 1; g0(0,1) = 2; g0(1,0) = 3; g0(1,1) = 0;
+ g1(0,0) = 4; g1(0,1) = 2; g1(1,0) = 3; g1(1,1) = 5;
+
+ // Generate the complex, initialize the vineyard (which also computes the pairing)
+ typedef LSVineyard<Grid2D::CoordinateIndex, Grid2D> Grid2DVineyard;
+
+ Grid2DVineyard::LSFiltration simplices;
+ g0.complex_generator(make_push_back_functor(simplices));
+ Grid2DVineyard::VertexComparison vcmp(g0);
+ Grid2DVineyard::SimplexComparison scmp(vcmp);
+ simplices.sort(scmp);
+ std::cout << "Complex generated, size: " << simplices.size() << std::endl;
+ std::copy(simplices.begin(), simplices.end(), std::ostream_iterator<Grid2D::Smplx>(std::cout, "\n"));
+
+ Grid2DVineyard v(g0.begin(), g0.end(), simplices, g0);
+ std::cout << "Filtration generated, size: " << v.filtration().size() << std::endl;
+ std::cout << "Pairing computed" << std::endl;
+
+ // Simplex order before
+ std::cout << "Simplex order:" << std::endl;
+ for (Grid2DVineyard::LSFiltration::Index cur = v.filtration().begin(); cur != v.filtration().end(); ++cur)
+ std::cout << " " << v.filtration().simplex(cur) << std::endl;
+
+ // Compute vineyard
+ v.compute_vineyard(g1);
+ std::cout << "Vineyard computed" << std::endl;
+
+ // Simplex order after
+ std::cout << "Simplex order:" << std::endl;
+ for (Grid2DVineyard::LSFiltration::Index cur = v.filtration().begin(); cur != v.filtration().end(); ++cur)
+ std::cout << " " << v.filtration().simplex(cur) << std::endl;
+
+ v.vineyard().save_edges("test-vineyard");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pl-functions/test-grid2D.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,16 @@
+#include "grid2D.h"
+#include <iostream>
+
+int main()
+{
+ Grid2D grid(40,60);
+ int i = 0;
+ for (int x = 0; x < 40; ++x)
+ for (int y = 0; y < 60; ++y)
+ {
+ grid(x,y) = i++;
+ }
+
+ std::cout << grid(20,30) << std::endl;
+ std::cout << grid << std::endl;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/poincare/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,7 @@
+set (targets
+ poincare)
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/poincare/poincare-filtration.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,392 @@
+9
+2
+2 9
+4
+4 9
+2 4
+2 4 9
+1
+1 9
+1 4
+1 4 9
+1 2
+1 2 9
+1 2 4
+1 2 4 9
+15
+2 15
+4 15
+2 4 15
+1 15
+1 4 15
+1 2 15
+1 2 4 15
+14
+2 14
+6
+6 14
+2 6
+2 6 14
+1 14
+1 6
+1 6 14
+1 2 14
+1 2 6
+1 2 6 14
+6 15
+2 6 15
+1 6 15
+1 2 6 15
+9 14
+2 9 14
+1 9 14
+1 2 9 14
+12
+3
+3 12
+4 12
+3 4
+3 4 12
+1 12
+1 4 12
+1 3
+1 3 12
+1 3 4
+1 3 4 12
+3 15
+3 4 15
+1 3 15
+1 3 4 15
+10
+3 10
+7
+7 10
+3 7
+3 7 10
+1 10
+1 7
+1 7 10
+1 3 10
+1 3 7
+1 3 7 10
+7 12
+3 7 12
+1 7 12
+1 3 7 12
+10 15
+3 10 15
+1 10 15
+1 3 10 15
+9 12
+4 9 12
+1 9 12
+1 4 9 12
+13
+5
+5 13
+6 13
+5 6
+5 6 13
+1 13
+1 6 13
+1 5
+1 5 13
+1 5 6
+1 5 6 13
+5 14
+5 6 14
+1 5 14
+1 5 6 14
+11
+5 11
+8
+8 11
+5 8
+5 8 11
+1 11
+1 8
+1 8 11
+1 5 11
+1 5 8
+1 5 8 11
+8 13
+5 8 13
+1 8 13
+1 5 8 13
+11 14
+5 11 14
+1 11 14
+1 5 11 14
+13 15
+6 13 15
+1 13 15
+1 6 13 15
+8 10
+7 8
+7 8 10
+1 8 10
+1 7 8
+1 7 8 10
+7 11
+7 8 11
+1 7 11
+1 7 8 11
+11 12
+7 11 12
+1 11 12
+1 7 11 12
+10 13
+8 10 13
+1 10 13
+1 8 10 13
+9 11
+9 11 12
+1 9 11
+1 9 11 12
+9 11 14
+1 9 11 14
+10 13 15
+1 10 13 15
+5 10
+3 5
+3 5 10
+2 10
+2 5
+2 5 10
+2 3
+2 3 10
+2 3 5
+2 3 5 10
+3 11
+3 5 11
+2 11
+2 5 11
+2 3 11
+2 3 5 11
+2 7
+2 7 10
+2 3 7
+2 3 7 10
+3 13
+7 13
+3 7 13
+2 13
+2 7 13
+2 3 13
+2 3 7 13
+11 13
+3 11 13
+2 11 13
+2 3 11 13
+4 13
+9 13
+4 9 13
+2 9 13
+2 4 13
+2 4 9 13
+4 11
+4 11 13
+2 4 11
+2 4 11 13
+11 15
+4 11 15
+2 11 15
+2 4 11 15
+2 8
+2 8 11
+2 5 8
+2 5 8 11
+5 12
+8 12
+5 8 12
+2 12
+2 8 12
+2 5 12
+2 5 8 12
+10 12
+5 10 12
+2 10 12
+2 5 10 12
+6 12
+6 10
+6 10 12
+2 6 12
+2 6 10
+2 6 10 12
+10 14
+6 10 14
+2 10 14
+2 6 10 14
+12 15
+6 12 15
+2 12 15
+2 6 12 15
+7 9
+7 9 13
+2 7 9
+2 7 9 13
+7 14
+7 9 14
+2 7 14
+2 7 9 14
+7 10 14
+2 7 10 14
+8 15
+8 11 15
+2 8 15
+2 8 11 15
+8 12 15
+2 8 12 15
+4 14
+4 5
+4 5 14
+3 14
+3 5 14
+3 4 14
+3 4 5
+3 4 5 14
+5 15
+4 5 15
+3 5 15
+3 4 5 15
+12 14
+4 12 14
+3 12 14
+3 4 12 14
+5 10 15
+3 5 10 15
+3 11 14
+3 5 11 14
+12 13
+7 12 13
+3 12 13
+3 7 12 13
+13 14
+11 13 14
+3 13 14
+3 11 13 14
+12 13 14
+3 12 13 14
+5 7
+6 7
+5 6 7
+4 7
+4 6
+4 6 7
+4 5 7
+4 5 6
+4 5 6 7
+4 6 14
+4 5 6 14
+7 15
+5 7 15
+4 7 15
+4 5 7 15
+6 11
+6 7 11
+4 7 11
+4 6 11
+4 6 7 11
+10 11
+6 10 11
+4 10
+4 10 11
+4 6 10
+4 6 10 11
+4 10 14
+4 6 10 14
+7 11 15
+4 7 11 15
+8 9
+8 9 12
+4 8
+4 8 12
+4 8 9
+4 8 9 12
+8 9 13
+4 8 13
+4 8 9 13
+4 10 13
+4 8 10
+4 8 10 13
+8 14
+8 10 14
+4 8 14
+4 8 10 14
+8 12 14
+4 8 12 14
+10 11 13
+4 10 11 13
+6 7 13
+5 7 13
+5 6 7 13
+5 9
+5 9 13
+5 7 9
+5 7 9 13
+9 15
+7 9 15
+5 9 15
+5 7 9 15
+5 9 12
+5 8 9
+5 8 9 12
+5 8 9 13
+9 10
+9 10 12
+5 9 10
+5 9 10 12
+9 10 15
+5 9 10 15
+6 11 12
+6 7 12
+6 7 11 12
+6 12 13
+6 7 12 13
+10 11 12
+6 10 11 12
+12 13 15
+6 12 13 15
+7 8 14
+7 8 10 14
+7 8 15
+7 8 11 15
+14 15
+8 14 15
+7 14 15
+7 8 14 15
+9 14 15
+7 9 14 15
+12 14 15
+8 12 14 15
+9 10 11
+9 10 11 12
+16
+10 16
+11 16
+10 11 16
+9 16
+9 11 16
+9 10 16
+9 10 11 16
+15 16
+10 15 16
+9 15 16
+9 10 15 16
+14 16
+11 14 16
+9 14 16
+9 11 14 16
+14 15 16
+9 14 15 16
+13 16
+11 13 16
+10 13 16
+10 11 13 16
+13 15 16
+10 13 15 16
+13 14 16
+11 13 14 16
+13 14 15
+12 13 14 15
+13 14 15 16
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/poincare/poincare.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,86 @@
+#include "topology/simplex.h"
+#include "topology/filtration.h"
+#include "topology/static-persistence.h"
+#include "topology/persistence-diagram.h"
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <boost/program_options.hpp>
+
+typedef Simplex<unsigned, unsigned> Smplx;
+typedef Filtration<Smplx> Fltr;
+typedef StaticPersistence<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+
+namespace po = boost::program_options;
+
+int main(int argc, char** argv)
+{
+ std::string infilename;
+
+ // Parse program options
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename),
+ "Filtration filename");
+
+ po::options_description visible("Allowed options");
+ visible.add_options()
+ ("help,h", "produce help message");
+ po::positional_options_description p;
+ p.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] FILENAME" << std::endl;
+ std::cout << visible << std::endl;
+ return 1;
+ }
+
+
+ Fltr f;
+ std::ifstream in(infilename.c_str());
+ unsigned int i = 0;
+ std::string s;
+ std::getline(in, s);
+ while(in)
+ {
+ std::istringstream linestream(s);
+ Smplx simplex(i++);
+ unsigned int vertex;
+ linestream >> vertex;
+ while(linestream)
+ {
+ simplex.add(vertex);
+ linestream >> vertex;
+ }
+ std::cout << simplex << std::endl;
+ f.push_back(simplex);
+ std::getline(in, s);
+ }
+
+ f.sort(Smplx::DataComparison());
+ Persistence pers(f);
+ pers.pair_simplices();
+
+ Persistence::SimplexMap<Fltr> m = pers.make_simplex_map(f);
+ std::map<Dimension, PDgm> dgms;
+ init_diagrams(dgms, pers.begin(), pers.end(),
+ evaluate_through_map(m, Smplx::DataEvaluator()),
+ evaluate_through_map(m, Smplx::DimensionExtractor()));
+
+ std::cout << 0 << std::endl << dgms[0] << std::endl;
+ std::cout << 1 << std::endl << dgms[1] << std::endl;
+ std::cout << 2 << std::endl << dgms[2] << std::endl;
+ std::cout << 3 << std::endl << dgms[3] << std::endl;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/poincare/poincare.dat Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,99 @@
+# 16 vertex triangulation of Poincare sphere
+# made by Bjo"rner and Lutz,
+# in "Simplicial manifolds, bistellar flips and a 16-vertex
+# triangulation of the Poincar\'e homology 3-sphere",
+# to appear in Experimental Mathematics.
+# This example has f = (1, 16, 106, 180, 90).
+#
+# Found on Masahiro Hachimori's page at
+# http://infoshako.sk.tsukuba.ac.jp/~hachi/math/library/poincare_eng.html
+1 2 4 9
+1 2 4 15
+1 2 6 14
+1 2 6 15
+1 2 9 14
+1 3 4 12
+1 3 4 15
+1 3 7 10
+1 3 7 12
+1 3 10 15
+1 4 9 12
+1 5 6 13
+1 5 6 14
+1 5 8 11
+1 5 8 13
+1 5 11 14
+1 6 13 15
+1 7 8 10
+1 7 8 11
+1 7 11 12
+1 8 10 13
+1 9 11 12
+1 9 11 14
+1 10 13 15
+2 3 5 10
+2 3 5 11
+2 3 7 10
+2 3 7 13
+2 3 11 13
+2 4 9 13
+2 4 11 13
+2 4 11 15
+2 5 8 11
+2 5 8 12
+2 5 10 12
+2 6 10 12
+2 6 10 14
+2 6 12 15
+2 7 9 13
+2 7 9 14
+2 7 10 14
+2 8 11 15
+2 8 12 15
+3 4 5 14
+3 4 5 15
+3 4 12 14
+3 5 10 15
+3 5 11 14
+3 7 12 13
+3 11 13 14
+3 12 13 14
+4 5 6 7
+4 5 6 14
+4 5 7 15
+4 6 7 11
+4 6 10 11
+4 6 10 14
+4 7 11 15
+4 8 9 12
+4 8 9 13
+4 8 10 13
+4 8 10 14
+4 8 12 14
+4 10 11 13
+5 6 7 13
+5 7 9 13
+5 7 9 15
+5 8 9 12
+5 8 9 13
+5 9 10 12
+5 9 10 15
+6 7 11 12
+6 7 12 13
+6 10 11 12
+6 12 13 15
+7 8 10 14
+7 8 11 15
+7 8 14 15
+7 9 14 15
+8 12 14 15
+9 10 11 12
+9 10 11 16
+9 10 15 16
+9 11 14 16
+9 14 15 16
+10 11 13 16
+10 13 15 16
+11 13 14 16
+12 13 14 15
+13 14 15 16
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,16 @@
+set (targets
+ rips
+ rips-pairwise
+ rips-weighted
+ rips-image-zigzag
+ rips-zigzag)
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY})
+endforeach (t ${targets})
+
+add_custom_target (rips.py ALL
+ ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rips.py ${CMAKE_CURRENT_BINARY_DIR}/rips.py)
+add_custom_target (rips-pairwise.py ALL
+ ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rips-pairwise.py ${CMAKE_CURRENT_BINARY_DIR}/rips-pairwise.py)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/rips-image-zigzag.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,452 @@
+#include <topology/rips.h>
+#include <topology/image-zigzag-persistence.h>
+#include <utilities/types.h>
+#include <utilities/containers.h>
+
+#include <utilities/log.h>
+#include <utilities/memory.h> // for report_memory()
+#include <utilities/timer.h>
+
+#include <geometry/l2distance.h> // for L2Distance and read_points()
+#include <geometry/distances.h>
+
+#include <map>
+#include <cmath>
+#include <fstream>
+#include <stack>
+#include <cstdlib>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+
+#ifdef COUNTERS
+static Counter* cComplexSize = GetCounter("rips/size");
+static Counter* cOperations = GetCounter("rips/operations");
+#endif // COUNTERS
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+
+typedef PairDistances::IndexType Vertex;
+typedef Simplex<Vertex> Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef std::list<Smplx> SimplexList;
+typedef std::set<Smplx, Smplx::VertexDimensionComparison> SimplexSet;
+
+typedef std::vector<Vertex> VertexVector;
+typedef std::vector<DistanceType> EpsilonVector;
+typedef std::vector<std::pair<Vertex, Vertex> > EdgeVector;
+
+typedef Rips<PairDistances, Smplx> RipsGenerator;
+typedef RipsGenerator::Evaluator SimplexEvaluator;
+
+struct BirthInfo;
+typedef ImageZigzagPersistence<BirthInfo> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef std::map<Smplx, Index,
+ Smplx::VertexDimensionComparison> Complex;
+typedef Zigzag::ZColumn Boundary;
+
+// Information we need to know when a class dies
+struct BirthInfo
+{
+ BirthInfo(DistanceType dist = DistanceType(), Dimension dim = Dimension()):
+ distance(dist), dimension(dim) {}
+ DistanceType distance;
+ Dimension dimension;
+};
+
+// Forward declarations of auxilliary functions
+void report_death(std::ofstream& out, Death d, DistanceType epsilon, Dimension skeleton_dimension);
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b);
+void show_image_betti(Zigzag& zz, Dimension skeleton);
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi);
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& from_multiplier,
+ float& to_multiplier,
+ std::string& infilename,
+ std::string& outfilename);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Timer total, remove, add, ec, vc;
+ total.start();
+
+#if 0
+ SetFrequency(cOperations, 25000);
+ SetTrigger(cOperations, cComplexSize);
+#endif
+
+ unsigned skeleton_dimension;
+ float from_multiplier, to_multiplier;
+ std::string infilename, outfilename;
+ process_command_line_options(argc, argv, skeleton_dimension, from_multiplier, to_multiplier, infilename, outfilename);
+
+ // Read in points
+ PointContainer points;
+ read_points(infilename, points);
+
+ // Create output file
+ std::ofstream out(outfilename.c_str());
+
+ // Create pairwise distances
+ PairDistances distances(points);
+
+ // Order vertices and epsilons (in maxmin fashion)
+ VertexVector vertices;
+ EpsilonVector epsilons;
+ EdgeVector edges;
+
+ {
+ EpsilonVector dist(distances.size(), Infinity);
+
+ vertices.push_back(distances.begin());
+ //epsilons.push_back(Infinity);
+ while (vertices.size() < distances.size())
+ {
+ for (Vertex v = distances.begin(); v != distances.end(); ++v)
+ dist[v] = std::min(dist[v], distances(v, vertices.back()));
+ EpsilonVector::const_iterator max = std::max_element(dist.begin(), dist.end());
+ vertices.push_back(max - dist.begin());
+ epsilons.push_back(*max);
+ }
+ epsilons.push_back(0);
+ }
+
+ rInfo("Point and epsilon ordering:");
+ for (unsigned i = 0; i < vertices.size(); ++i)
+ rInfo(" %4d: %4d - %f", i, vertices[i], epsilons[i]);
+
+ // Generate and sort all the edges
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ {
+ Vertex u = vertices[i];
+ Vertex v = vertices[j];
+ if (distances(u,v) <= to_multiplier*epsilons[j-1])
+ edges.push_back(std::make_pair(u,v));
+ }
+ std::sort(edges.begin(), edges.end(), RipsGenerator::ComparePair(distances));
+ rInfo("Total participating edges: %d", edges.size());
+ for (EdgeVector::const_iterator cur = edges.begin(); cur != edges.end(); ++cur)
+ rDebug(" (%d, %d) %f", cur->first, cur->second, distances(cur->first, cur->second));
+
+ // Construct zigzag
+ Complex complex;
+ Zigzag zz;
+ RipsGenerator rips(distances);
+ SimplexEvaluator size(distances);
+
+ // Insert vertices
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ {
+ // Add a vertex
+ Smplx sv; sv.add(vertices[i]);
+ rDebug("Adding %s", tostring(sv).c_str());
+ add.start();
+ complex.insert(std::make_pair(sv,
+ zz.add(Boundary(),
+ true, // vertex is always in the subcomplex
+ BirthInfo(0, 0)).first));
+ add.stop();
+ //rDebug("Newly born cycle order: %d", complex[sv]->low->order);
+ CountNum(cComplexSize, 0);
+ Count(cComplexSize);
+ Count(cOperations);
+ }
+
+ rInfo("Commencing computation");
+ boost::progress_display show_progress(vertices.size());
+ unsigned sce = 0, // index of the current one past last edge in the subcomplex
+ ce = 0; // index of the current one past last edge in the complex
+ for (unsigned stage = 0; stage != vertices.size() - 1; ++stage)
+ {
+ unsigned i = vertices.size() - 1 - stage;
+ rInfo("Current stage %d: %d %f: %f -> %f", stage,
+ vertices[i], epsilons[i-1],
+ from_multiplier*epsilons[i-1],
+ to_multiplier*epsilons[i-1]);
+
+ /* Increase epsilon */
+ // Record the cofaces of all the simplices that need to be removed and reinserted
+ SimplexSet cofaces;
+ rDebug(" Cofaces size: %d", cofaces.size());
+ while(sce < ce)
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[sce];
+ if (distances(u,v) <= from_multiplier*epsilons[i-1])
+ ++sce;
+ else
+ break;
+
+ // Skip an edge if any one of its vertices has been removed from the complex
+ bool skip_edge = false;
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ if (u == vertices[j] || v == vertices[j])
+ {
+ // Debug only: eventually remove
+ rDebug(" Skipping edge (%d, %d)", u, v);
+ Smplx s; s.add(u); s.add(v);
+ AssertMsg(complex.find(s) == complex.end(), "Simplex should not be in the complex.");
+ skip_edge = true;
+ break;
+ }
+ if (skip_edge) continue;
+ rDebug(" Generating cofaces for (%d, %d)", u, v);
+
+ ec.start();
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ to_multiplier*epsilons[i],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ ec.stop();
+ }
+ rDebug(" Recorded cofaces to remove");
+ rDebug(" Cofaces size: %d", cofaces.size());
+ // Remove all the cofaces
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing %s", tostring(*cur).c_str());
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ AssertMsg(!si->second->subcomplex, "We should not remove simplices already in the subcomplex when we increase epsilon");
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(out, d, epsilons[i-1], skeleton_dimension);
+ }
+ rDebug(" Removed cofaces");
+
+ // Add anything else that needs to be inserted into the complex
+ while (ce < edges.size())
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[ce];
+ if (distances(u,v) <= to_multiplier*epsilons[i-1])
+ ++ce;
+ else
+ break;
+ rDebug(" Recording cofaces of edges[%d]=(%d, %d) with size=%f", (ce-1), u, v, distances(u,v));
+ ec.start();
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ to_multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ ec.stop();
+ }
+ rDebug(" Recorded new cofaces to add");
+
+ // Progress sce
+ while (sce < ce)
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[sce];
+ rDebug(" Progressing sce=%d over (%d, %d) %f", sce, u, v, distances(u,v));
+ if (distances(u,v) <= from_multiplier*epsilons[i-1])
+ ++sce;
+ else
+ break;
+ }
+ rDebug(" Moved subcomplex index forward");
+
+ // Insert all the cofaces
+ rDebug(" Cofaces size: %d", cofaces.size());
+ for (SimplexSet::const_iterator cur = cofaces.begin(); cur != cofaces.end(); ++cur)
+ {
+ Index idx; Death d; Boundary b;
+ rDebug(" Adding %s, its size %f", tostring(*cur).c_str(), size(*cur));
+ make_boundary(*cur, complex, zz, b);
+ add.start();
+ boost::tie(idx, d) = zz.add(b,
+ size(*cur) <= from_multiplier*epsilons[i-1],
+ BirthInfo(epsilons[i-1], cur->dimension()));
+ add.stop();
+ //if (!d) rDebug("Newly born cycle order: %d", complex[*cur]->low->order);
+ CountNum(cComplexSize, cur->dimension());
+ Count(cComplexSize);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ complex.insert(std::make_pair(*cur, idx));
+ report_death(out, d, epsilons[i-1], skeleton_dimension);
+ }
+ rInfo("Increased epsilon; complex size: %d", complex.size());
+ show_image_betti(zz, skeleton_dimension);
+ report_memory();
+
+ /* Remove the vertex */
+ cofaces.clear();
+ rDebug(" Cofaces size: %d", cofaces.size());
+ vc.start();
+ rips.vertex_cofaces(vertices[i],
+ skeleton_dimension,
+ to_multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ vc.stop();
+ rDebug(" Computed cofaces of the vertex, their number: %d", cofaces.size());
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing: %s", tostring(*cur).c_str());
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(out, d, epsilons[i-1], skeleton_dimension);
+ }
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ for (Complex::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ rDebug(" %s", tostring(cur->first).c_str());
+ show_image_betti(zz, skeleton_dimension);
+ report_memory();
+
+ ++show_progress;
+ }
+
+ // Remove the last vertex
+ AssertMsg(complex.size() == 1, "Only one vertex must remain");
+ remove.start();
+ Death d = zz.remove(complex.begin()->second, BirthInfo(epsilons[0], -1));
+ remove.stop();
+ complex.erase(complex.begin());
+ if (!d) AssertMsg(false, "The vertex must have died");
+ report_death(out, d, epsilons[0], skeleton_dimension);
+ CountNumBy(cComplexSize, 0, -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ ++show_progress;
+
+ total.stop();
+
+ remove.check("Remove timer ");
+ add.check ("Add timer ");
+ ec.check ("Edge coface timer ");
+ vc.check ("Vertex coface timer ");
+ total.check ("Total timer ");
+}
+
+
+
+void report_death(std::ofstream& out, Death d, DistanceType epsilon, Dimension skeleton_dimension)
+{
+ if (d && ((d->distance - epsilon) != 0) && (d->dimension < skeleton_dimension))
+ out << d->dimension << " " << d->distance << " " << epsilon << std::endl;
+}
+
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b)
+{
+ rDebug(" Boundary of <%s>", tostring(s).c_str());
+ for (Smplx::BoundaryIterator cur = s.boundary_begin(); cur != s.boundary_end(); ++cur)
+ {
+ b.append(c[*cur], zz.cmp);
+ rDebug(" %d (inL=%d)", c[*cur]->order, b.back()->subcomplex);
+ }
+}
+
+bool face_leaving_subcomplex(Complex::reverse_iterator si, const SimplexEvaluator& size, DistanceType after, DistanceType before)
+{
+ const Smplx& s = si->first;
+ for (Smplx::VertexContainer::const_iterator v1 = s.vertices().begin(); v1 != s.vertices().end(); ++v1)
+ for (Smplx::VertexContainer::const_iterator v2 = boost::next(v1); v2 != s.vertices().end(); ++v2)
+ {
+ Smplx e; e.add(*v1); e.add(*v2);
+ if (size(e) > after && size(e) <= before)
+ return true;
+ }
+
+ return false;
+}
+
+void show_image_betti(Zigzag& zz, Dimension skeleton)
+{
+ for (Zigzag::ZIndex cur = zz.image_begin(); cur != zz.image_end(); ++cur)
+ if (cur->low == zz.boundary_end() && cur->birth.dimension < skeleton)
+ rInfo("Class in the image of dimension: %d", cur->birth.dimension);
+}
+
+
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi)
+{ return (out << bi.distance); }
+
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& from_multiplier,
+ float& to_multiplier,
+ std::string& infilename,
+ std::string& outfilename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Location to save persistence pairs");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<unsigned>(&skeleton_dimension)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("from,f", po::value<float>(&from_multiplier)->default_value(4), "From multiplier for the epsilon (distance to next maxmin point) when computing the Rips complex")
+ ("to,t", po::value<float>(&to_multiplier)->default_value(16), "To multiplier for the epsilon (distance to next maxmin point) when computing the Rips complex");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file output-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/rips-pairwise.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,147 @@
+#include <topology/rips.h>
+#include <topology/filtration.h>
+#include <topology/static-persistence.h>
+#include <topology/dynamic-persistence.h>
+#include <topology/persistence-diagram.h>
+
+#include <geometry/l2distance.h>
+#include <geometry/distances.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+#include <utilities/timer.h>
+
+#include <vector>
+
+#include <boost/program_options.hpp>
+
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+typedef PairDistances::IndexType Vertex;
+
+typedef Rips<PairDistances> Generator;
+typedef Generator::Simplex Smplx;
+typedef Filtration<Smplx> Fltr;
+// typedef StaticPersistence<> Persistence;
+typedef DynamicPersistenceChains<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, std::string& diagram_name);
+
+int main(int argc, char* argv[])
+{
+ Dimension skeleton;
+ DistanceType max_distance;
+ std::string infilename, diagram_name;
+
+ program_options(argc, argv, infilename, skeleton, max_distance, diagram_name);
+ std::ofstream diagram_out(diagram_name.c_str());
+ std::cout << "Diagram: " << diagram_name << std::endl;
+
+ PointContainer points;
+ read_points(infilename, points);
+
+ PairDistances distances(points);
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ Fltr f;
+
+ // Generate 2-skeleton of the Rips complex for epsilon = 50
+ rips.generate(skeleton, max_distance, make_push_back_functor(f));
+ std::cout << "# Generated complex of size: " << f.size() << std::endl;
+
+ // Generate filtration with respect to distance and compute its persistence
+ f.sort(Generator::Comparison(distances));
+
+ Timer persistence_timer; persistence_timer.start();
+ Persistence p(f);
+ p.pair_simplices();
+ persistence_timer.stop();
+
+#if 1
+ // Output cycles
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(f);
+ for (Persistence::iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ const Persistence::Cycle& cycle = cur->cycle;
+
+ if (!cur->sign()) // only negative simplices have non-empty cycles
+ {
+ Persistence::OrderIndex birth = cur->pair; // the cycle that cur killed was born when we added birth (another simplex)
+
+ const Smplx& b = m[birth];
+ const Smplx& d = m[cur];
+
+ // if (b.dimension() != 1) continue;
+ // std::cout << "Pair: (" << size(b) << ", " << size(d) << ")" << std::endl;
+ if (b.dimension() >= skeleton) continue;
+ diagram_out << b.dimension() << " " << size(b) << " " << size(d) << std::endl;
+ } else if (cur->unpaired()) // positive could be unpaired
+ {
+ const Smplx& b = m[cur];
+ // if (b.dimension() != 1) continue;
+
+ // std::cout << "Unpaired birth: " << size(b) << std::endl;
+ // cycle = cur->chain; // TODO
+ if (b.dimension() >= skeleton) continue;
+ diagram_out << b.dimension() << " " << size(b) << " inf" << std::endl;
+ }
+
+ // Iterate over the cycle
+ // for (Persistence::Cycle::const_iterator si = cycle.begin();
+ // si != cycle.end(); ++si)
+ // {
+ // const Smplx& s = m[*si];
+ // //std::cout << s.dimension() << std::endl;
+ // const Smplx::VertexContainer& vertices = s.vertices(); // std::vector<Vertex> where Vertex = Distances::IndexType
+ // AssertMsg(vertices.size() == s.dimension() + 1, "dimension of a simplex is one less than the number of its vertices");
+ // std::cout << vertices[0] << " " << vertices[1] << std::endl;
+ // }
+ }
+#endif
+
+ persistence_timer.check("# Persistence timer");
+}
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance, std::string& diagram_name)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<Dimension>(&skeleton)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("max-distance,m", po::value<DistanceType>(&max_distance)->default_value(Infinity), "Maximum value for the Rips complex construction")
+ ("diagram,d", po::value<std::string>(&diagram_name), "Filename where to output the persistence diagram");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+#endif
+
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/rips-pairwise.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,58 @@
+#/usr/bin/env python
+
+
+from dionysus import Rips, PairwiseDistances, StaticPersistence, Filtration, points_file, \
+ ExplicitDistances, data_dim_cmp
+from sys import argv, exit
+import time
+
+def main(filename, skeleton, max):
+ points = [p for p in points_file(filename)]
+ distances = PairwiseDistances(points)
+ # distances = ExplicitDistances(distances) # speeds up generation of the Rips complex at the expense of memory usage
+ rips = Rips(distances)
+ print time.asctime(), "Rips initialized"
+
+ simplices = Filtration()
+ rips.generate(skeleton, max, simplices.append)
+ print time.asctime(), "Generated complex: %d simplices" % len(simplices)
+
+ # While this step is unnecessary (Filtration below can be passed rips.cmp),
+ # it greatly speeds up the running times
+ for s in simplices: s.data = rips.eval(s)
+ print time.asctime(), simplices[0], '...', simplices[-1]
+
+ simplices.sort(data_dim_cmp) # could be rips.cmp if s.data for s in simplices is not set
+ print time.asctime(), "Set up filtration"
+
+ p = StaticPersistence(simplices)
+ print time.asctime(), "Initialized StaticPersistence"
+
+ p.pair_simplices()
+ print time.asctime(), "Simplices paired"
+
+ print "Outputting persistence diagram"
+ smap = p.make_simplex_map(simplices)
+ for i in p:
+ if i.sign():
+ b = smap[i]
+
+ if b.dimension() >= skeleton: continue
+
+ if i.unpaired():
+ print b.dimension(), b.data, "inf"
+ continue
+
+ d = smap[i.pair()]
+ print b.dimension(), b.data, d.data
+
+if __name__ == '__main__':
+ if len(argv) < 4:
+ print "Usage: %s POINTS SKELETON MAX" % argv[0]
+ exit()
+
+ filename = argv[1]
+ skeleton = int(argv[2])
+ max = float(argv[3])
+
+ main(filename, skeleton, max)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/rips-weighted.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,144 @@
+#include <topology/weighted-rips.h>
+#include <topology/filtration.h>
+#include <topology/static-persistence.h>
+#include <topology/dynamic-persistence.h>
+#include <topology/persistence-diagram.h>
+
+//#define RIPS_CLOSURE_CECH_SKELETON
+#ifndef RIPS_CLOSURE_CECH_SKELETON
+#include <geometry/weighted-l2distance.h>
+#else
+#include <geometry/weighted-cechdistance.h>
+#endif
+
+#include <geometry/distances.h>
+
+#include <utilities/containers.h> // for BackInsertFunctor
+#include <utilities/timer.h>
+
+#include <vector>
+
+#include <boost/program_options.hpp>
+
+
+typedef PairwiseDistances<PointContainer, WeightedL2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+typedef PairDistances::IndexType Vertex;
+
+typedef WeightedRips<PairDistances> Generator;
+typedef Generator::Simplex Smplx;
+typedef Filtration<Smplx> Fltr;
+typedef StaticPersistence<> Persistence;
+//typedef DynamicPersistenceChains<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Dimension skeleton;
+ DistanceType max_distance;
+ std::string infilename;
+
+ program_options(argc, argv, infilename, skeleton, max_distance);
+
+ PointContainer points;
+ read_weighted_points(infilename, points);
+
+ PairDistances distances(points);
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ Generator::Comparison cmp (distances);
+ Fltr complex;
+
+ // Generate skeleton of the weighted Rips complex for epsilon = 50
+ rips.generate(skeleton, max_distance, make_push_back_functor(complex));
+ std::cout << "# Generated complex of size: " << complex.size() << std::endl;
+
+ // Generate filtration with respect to distance and compute its persistence
+ complex.sort(cmp);
+
+ Timer persistence_timer; persistence_timer.start();
+ Persistence p(complex);
+ p.pair_simplices();
+ persistence_timer.stop();
+
+ // Output cycles
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(complex);
+ for (Persistence::iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ const Persistence::Cycle& cycle = cur->cycle;
+
+ if (!cur->sign()) // only negative simplices have non-empty cycles
+ {
+ Persistence::OrderIndex birth = cur->pair; // the cycle that cur killed was born when we added birth (another simplex)
+
+ const Smplx& b = m[birth];
+ const Smplx& d = m[cur];
+
+ if (b.dimension() >= skeleton) continue;
+ std::cout << b.dimension() << " " << size(b) << " " << size(d) << std::endl;
+ } else if (cur->unpaired()) // positive could be unpaired
+ {
+ const Smplx& b = m[cur];
+ if (b.dimension() >= skeleton) continue;
+
+ std::cout << b.dimension() << " " << size(b) << " inf" << std::endl;
+ //cycle = cur->chain;
+ }
+ }
+
+ persistence_timer.check("# Persistence timer");
+}
+
+void program_options(int argc, char* argv[], std::string& infilename, Dimension& skeleton, DistanceType& max_distance)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose weighed Rips zigzag we want to compute");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<Dimension>(&skeleton)->default_value(2), "Dimension of the weighted Rips complex we want to compute")
+ ("max-distance,m", po::value<DistanceType>(&max_distance)->default_value(Infinity), "Maximum value for the weighted Rips complex construction");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("input-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/rips-zigzag.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,353 @@
+#include <topology/rips.h>
+#include <topology/zigzag-persistence.h>
+#include <utilities/types.h>
+#include <utilities/containers.h>
+
+#include <geometry/l2distance.h> // Point, PointContainer, L2DistanceType, read_points
+#include <geometry/distances.h>
+
+#include <utilities/log.h>
+#include <utilities/memory.h> // for report_memory()
+#include <utilities/timer.h>
+
+#include <map>
+#include <cmath>
+#include <fstream>
+#include <stack>
+#include <cstdlib>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/program_options.hpp>
+#include <boost/progress.hpp>
+
+#ifdef COUNTERS
+static Counter* cComplexSize = GetCounter("rips/size");
+static Counter* cOperations = GetCounter("rips/operations");
+#endif // COUNTERS
+
+typedef PairwiseDistances<PointContainer, L2Distance> PairDistances;
+typedef PairDistances::DistanceType DistanceType;
+
+typedef PairDistances::IndexType Vertex;
+typedef Simplex<Vertex> Smplx;
+typedef std::vector<Smplx> SimplexVector;
+typedef std::list<Smplx> SimplexList;
+typedef std::set<Smplx, Smplx::VertexDimensionComparison> SimplexSet;
+
+typedef std::vector<Vertex> VertexVector;
+typedef std::vector<DistanceType> EpsilonVector;
+typedef std::vector<std::pair<Vertex, Vertex> > EdgeVector;
+
+typedef Rips<PairDistances, Smplx> RipsGenerator;
+typedef RipsGenerator::Evaluator SimplexEvaluator;
+
+struct BirthInfo;
+typedef ZigzagPersistence<BirthInfo> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef std::map<Smplx, Index,
+ Smplx::VertexDimensionComparison> Complex;
+typedef Zigzag::ZColumn Boundary;
+
+// Information we need to know when a class dies
+struct BirthInfo
+{
+ BirthInfo(DistanceType dist = DistanceType(), Dimension dim = Dimension()):
+ distance(dist), dimension(dim) {}
+ DistanceType distance;
+ Dimension dimension;
+};
+
+// Forward declarations of auxilliary functions
+void report_death(std::ostream& out, Death d, DistanceType epsilon, Dimension skeleton_dimension);
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b);
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi);
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& multiplier,
+ std::string& infilename,
+ std::string& outfilename);
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+#endif
+
+ Timer total, remove, add, ec, vc;
+ total.start();
+
+#if 0
+ SetFrequency(cOperations, 25000);
+ SetTrigger(cOperations, cComplexSize);
+#endif
+
+ unsigned skeleton_dimension;
+ float multiplier;
+ std::string infilename, outfilename;
+ process_command_line_options(argc, argv, skeleton_dimension, multiplier, infilename, outfilename);
+
+ // Read in points
+ PointContainer points;
+ read_points(infilename, points);
+
+ // Create output file
+ std::ofstream out(outfilename.c_str());
+
+ // Create pairwise distances
+ PairDistances distances(points);
+
+ // Order vertices and epsilons (in maxmin fashion)
+ VertexVector vertices;
+ EpsilonVector epsilons;
+ EdgeVector edges;
+
+ {
+ EpsilonVector dist(distances.size(), Infinity);
+
+ vertices.push_back(distances.begin());
+ //epsilons.push_back(Infinity);
+ while (vertices.size() < distances.size())
+ {
+ for (Vertex v = distances.begin(); v != distances.end(); ++v)
+ dist[v] = std::min(dist[v], distances(v, vertices.back()));
+ EpsilonVector::const_iterator max = std::max_element(dist.begin(), dist.end());
+ vertices.push_back(max - dist.begin());
+ epsilons.push_back(*max);
+ }
+ epsilons.push_back(0);
+ }
+
+ rInfo("Point and epsilon ordering:");
+ for (unsigned i = 0; i < vertices.size(); ++i)
+ rInfo(" %4d: %4d - %f", i, vertices[i], epsilons[i]);
+
+ // Generate and sort all the edges
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ for (unsigned j = i+1; j != vertices.size(); ++j)
+ {
+ Vertex u = vertices[i];
+ Vertex v = vertices[j];
+ if (distances(u,v) <= multiplier*epsilons[j-1])
+ edges.push_back(std::make_pair(u,v));
+ }
+ std::sort(edges.begin(), edges.end(), RipsGenerator::ComparePair(distances));
+ rInfo("Total participating edges: %d", edges.size());
+ for (EdgeVector::const_iterator cur = edges.begin(); cur != edges.end(); ++cur)
+ rDebug(" (%d, %d) %f", cur->first, cur->second, distances(cur->first, cur->second));
+
+ // Construct zigzag
+ Complex complex;
+ Zigzag zz;
+ RipsGenerator rips(distances);
+ SimplexEvaluator size(distances);
+
+ // Insert vertices
+ for (unsigned i = 0; i != vertices.size(); ++i)
+ {
+ // Add a vertex
+ Smplx sv; sv.add(vertices[i]);
+ rDebug("Adding %s", tostring(sv).c_str());
+ add.start();
+ complex.insert(std::make_pair(sv,
+ zz.add(Boundary(),
+ BirthInfo(0, 0)).first));
+ add.stop();
+ //rDebug("Newly born cycle order: %d", complex[sv]->low->order);
+ CountNum(cComplexSize, 0);
+ Count(cComplexSize);
+ Count(cOperations);
+ }
+
+ rInfo("Commencing computation");
+ boost::progress_display show_progress(vertices.size());
+ unsigned ce = 0; // index of the current one past last edge in the complex
+ for (unsigned stage = 0; stage != vertices.size() - 1; ++stage)
+ {
+ unsigned i = vertices.size() - 1 - stage;
+ rInfo("Current stage %d: %d %f: %f", stage,
+ vertices[i], epsilons[i-1],
+ multiplier*epsilons[i-1]);
+
+ /* Increase epsilon */
+ // Record the cofaces of all the simplices that need to be removed and reinserted
+ SimplexSet cofaces;
+ rDebug(" Cofaces size: %d", cofaces.size());
+
+ // Add anything else that needs to be inserted into the complex
+ while (ce < edges.size())
+ {
+ Vertex u,v;
+ boost::tie(u,v) = edges[ce];
+ if (distances(u,v) <= multiplier*epsilons[i-1])
+ ++ce;
+ else
+ break;
+ rDebug(" Recording cofaces of edges[%d]=(%d, %d) with size=%f", (ce-1), u, v, distances(u,v));
+ ec.start();
+ rips.edge_cofaces(u, v,
+ skeleton_dimension,
+ multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ ec.stop();
+ }
+ rDebug(" Recorded new cofaces to add");
+
+ // Insert all the cofaces
+ rDebug(" Cofaces size: %d", cofaces.size());
+ for (SimplexSet::const_iterator cur = cofaces.begin(); cur != cofaces.end(); ++cur)
+ {
+ Index idx; Death d; Boundary b;
+ rDebug(" Adding %s, its size %f", tostring(*cur).c_str(), size(*cur));
+ make_boundary(*cur, complex, zz, b);
+ add.start();
+ boost::tie(idx, d) = zz.add(b,
+ BirthInfo(epsilons[i-1], cur->dimension()));
+ add.stop();
+ //if (!d) rDebug("Newly born cycle order: %d", complex[*cur]->low->order);
+ CountNum(cComplexSize, cur->dimension());
+ Count(cComplexSize);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ complex.insert(std::make_pair(*cur, idx));
+ report_death(out, d, epsilons[i-1], skeleton_dimension);
+ }
+ rInfo("Increased epsilon; complex size: %d", complex.size());
+ report_memory();
+
+ /* Remove the vertex */
+ cofaces.clear();
+ rDebug(" Cofaces size: %d", cofaces.size());
+ vc.start();
+ rips.vertex_cofaces(vertices[i],
+ skeleton_dimension,
+ multiplier*epsilons[i-1],
+ make_insert_functor(cofaces),
+ vertices.begin(),
+ vertices.begin() + i + 1);
+ vc.stop();
+ rDebug(" Computed cofaces of the vertex, their number: %d", cofaces.size());
+ for (SimplexSet::const_reverse_iterator cur = cofaces.rbegin(); cur != (SimplexSet::const_reverse_iterator)cofaces.rend(); ++cur)
+ {
+ rDebug(" Removing: %s", tostring(*cur).c_str());
+ Complex::iterator si = complex.find(*cur);
+ remove.start();
+ Death d = zz.remove(si->second,
+ BirthInfo(epsilons[i-1], cur->dimension() - 1));
+ remove.stop();
+ complex.erase(si);
+ CountNumBy(cComplexSize, cur->dimension(), -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ AssertMsg(zz.check_consistency(), "Zigzag representation must be consistent after removing a simplex");
+ report_death(out, d, epsilons[i-1], skeleton_dimension);
+ }
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ for (Complex::const_iterator cur = complex.begin(); cur != complex.end(); ++cur)
+ rDebug(" %s", tostring(cur->first).c_str());
+ report_memory();
+
+ ++show_progress;
+ }
+
+ // Remove the last vertex
+ AssertMsg(complex.size() == 1, "Only one vertex must remain");
+ remove.start();
+ Death d = zz.remove(complex.begin()->second, BirthInfo(epsilons[0], -1));
+ remove.stop();
+ complex.erase(complex.begin());
+ if (!d) AssertMsg(false, "The vertex must have died");
+ report_death(out, d, epsilons[0], skeleton_dimension);
+ CountNumBy(cComplexSize, 0, -1);
+ CountBy(cComplexSize, -1);
+ Count(cOperations);
+ rInfo("Removed vertex; complex size: %d", complex.size());
+ ++show_progress;
+
+ total.stop();
+
+ remove.check("Remove timer ");
+ add.check ("Add timer ");
+ ec.check ("Edge coface timer ");
+ vc.check ("Vertex coface timer ");
+ total.check ("Total timer ");
+}
+
+
+
+void report_death(std::ostream& out, Death d, DistanceType epsilon, Dimension skeleton_dimension)
+{
+ if (d && ((d->distance - epsilon) != 0) && (d->dimension < skeleton_dimension))
+ out << d->dimension << " " << d->distance << " " << epsilon << std::endl;
+}
+
+void make_boundary(const Smplx& s, Complex& c, const Zigzag& zz, Boundary& b)
+{
+ rDebug(" Boundary of <%s>", tostring(s).c_str());
+ for (Smplx::BoundaryIterator cur = s.boundary_begin(); cur != s.boundary_end(); ++cur)
+ {
+ b.append(c[*cur], zz.cmp);
+ rDebug(" %d", c[*cur]->order);
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const BirthInfo& bi)
+{ return (out << bi.distance); }
+
+void process_command_line_options(int argc,
+ char* argv[],
+ unsigned& skeleton_dimension,
+ float& multiplier,
+ std::string& infilename,
+ std::string& outfilename)
+{
+ namespace po = boost::program_options;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&infilename), "Point set whose Rips zigzag we want to compute")
+ ("output-file", po::value<std::string>(&outfilename), "Location to save persistence pairs");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("skeleton-dimsnion,s", po::value<unsigned>(&skeleton_dimension)->default_value(2), "Dimension of the Rips complex we want to compute")
+ ("multiplier,m", po::value<float>(&multiplier)->default_value(6), "Multiplier for the epsilon (distance to next maxmin point) when computing the Rips complex");
+#if LOGGING
+ std::vector<std::string> log_channels;
+ visible.add_options()
+ ("log,l", po::value< std::vector<std::string> >(&log_channels), "log channels to turn on (info, debug, etc)");
+#endif
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+ pos.add("output-file", 2);
+
+ po::options_description all; all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+#if LOGGING
+ for (std::vector<std::string>::const_iterator cur = log_channels.begin(); cur != log_channels.end(); ++cur)
+ stderrLog.subscribeTo( RLOG_CHANNEL(cur->c_str()) );
+ /**
+ * Interesting channels
+ * "info", "debug", "topology/persistence"
+ */
+#endif
+
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("output-file"))
+ {
+ std::cout << "Usage: " << argv[0] << " [options] input-file output-file" << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/rips.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,110 @@
+#include <topology/rips.h>
+#include <topology/filtration.h>
+#include <topology/static-persistence.h>
+#include <topology/dynamic-persistence.h>
+#include <topology/persistence-diagram.h>
+#include <utilities/containers.h> // for BackInsertFunctor
+
+#include <fstream>
+#include <boost/archive/binary_oarchive.hpp>
+#include <boost/serialization/map.hpp>
+
+// Trivial example of size() points on a line with integer coordinates
+struct Distances
+{
+ typedef int IndexType;
+ typedef double DistanceType;
+
+ DistanceType operator()(IndexType a, IndexType b) const { return std::abs(a - b); }
+
+ size_t size() const { return 2000; }
+ IndexType begin() const { return 0; }
+ IndexType end() const { return size(); }
+};
+
+//typedef Rips<ExplicitDistances<Distances> > Generator;
+typedef Rips<Distances> Generator;
+typedef Generator::Simplex Smplx;
+typedef Filtration<Smplx> Fltr;
+typedef StaticPersistence<> Persistence;
+// typedef DynamicPersistenceChains<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+
+
+int main(int argc, char* argv[])
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stdoutLog.subscribeTo( RLOG_CHANNEL("error") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("info") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("rips/info") );
+ //stdoutLog.subscribeTo( RLOG_CHANNEL("rips/debug") );
+#endif
+
+ Distances distances;
+
+ // Storing ExplicitDistances speeds up the computation (at the price of memory)
+ //ExplicitDistances<Distances> explicit_distances(distances);
+
+ Generator rips(distances);
+ Generator::Evaluator size(distances);
+ Fltr f;
+
+ // Generate 2-skeleton of the Rips complex for epsilon = 50
+ rips.generate(2, 10, make_push_back_functor(f));
+ rInfo("Generated complex of size: %d", f.size());
+
+ // Generate filtration with respect to distance and compute its persistence
+ f.sort(Generator::Comparison(distances));
+ Persistence p(f);
+ p.pair_simplices();
+ rInfo("Simplices paired");
+
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(f);
+
+ // Record the persistence intervals in the persistence diagrams
+ std::map<Dimension, PDgm> dgms;
+ init_diagrams(dgms, p.begin(), p.end(),
+ evaluate_through_map(m, size),
+ evaluate_through_map(m, Smplx::DimensionExtractor()));
+
+ // Serialize the diagrams to a file
+ std::ofstream ofs("rips-diagrams");
+ boost::archive::binary_oarchive oa(ofs);
+ oa << dgms;
+
+ // Output cycles
+ for (Persistence::iterator cur = p.begin(); cur != p.end(); ++cur)
+ {
+ const Persistence::Cycle& cycle = cur->cycle;
+
+ if (!cur->sign()) // only negative simplices have non-empty cycles
+ {
+ Persistence::OrderIndex birth = cur->pair; // the cycle that cur killed was born when we added birth (another simplex)
+
+ const Smplx& b = m[birth];
+ const Smplx& d = m[cur];
+
+ if (b.dimension() != 1) continue;
+ std::cout << "Pair: (" << size(b) << ", " << size(d) << ")" << std::endl;
+ } else if (cur->unpaired()) // positive could be unpaired
+ {
+ const Smplx& b = m[cur];
+ if (b.dimension() != 1) continue;
+
+ std::cout << "Unpaired birth: " << size(b) << std::endl;
+ // cycle = cur->chain;
+ }
+
+ // Iterate over the cycle
+ for (Persistence::Cycle::const_iterator si = cycle.begin(); si != cycle.end(); ++si)
+ {
+ const Smplx& s = m[*si];
+ //std::cout << s.dimension() << std::endl;
+ const Smplx::VertexContainer& vertices = s.vertices(); // std::vector<Vertex> where Vertex = Distances::IndexType
+ AssertMsg(vertices.size() == s.dimension() + 1, "dimension of a simplex is one less than the number of its vertices");
+ std::cout << vertices[0] << " " << vertices[1] << std::endl;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rips/rips.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,42 @@
+from math import fabs
+from dionysus import Rips, Filtration, StaticPersistence #, enable_log
+
+# Simple minded pairwise distance functor distance
+class Distances:
+ def __len__(self):
+ return 5
+
+ def __call__(self, x, y):
+ return fabs(y-x)
+
+dist = Distances()
+r = Rips(dist)
+lst = Filtration()
+lst2 = Filtration()
+
+#enable_log('rips')
+
+r.generate(1, 3, lst.append)
+r.generate(1, 3, lst2.append, [0,2,4])
+
+print "Rips complex on all vertices:", lst
+print "Rips complex on vertices [0,2,4]):", lst2
+
+print "Values:", [map(r.eval, lst)]
+print "Sorted:", sorted(lst, r.cmp)
+
+cofaces = []
+r.vertex_cofaces(2, 1, 3, cofaces.append)
+print "Cofaces of vertex 2:", cofaces
+
+cofaces = []
+r.vertex_cofaces(2, 1, 3, cofaces.append, [0,2,4])
+print "Cofaces of vertex 2 on vertices [0,2,4]:", cofaces
+
+f = lst
+f.sort(r.cmp)
+p = StaticPersistence(f)
+p.pair_simplices()
+smap = p.make_simplex_map(f)
+for s in p:
+ print smap[s], s.sign()
--- a/examples/triangle/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/triangle/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,7 +1,10 @@
-set (targets
- triangle)
-
-foreach (t ${targets})
- add_executable (${t} ${t}.cpp ${external_sources})
- target_link_libraries (${t} ${libraries})
-endforeach (t ${targets})
+set (targets
+ triangle
+ triangle-zigzag)
+
+set (libraries ${libraries} ${Boost_SERIALIZATION_LIBRARY})
+
+foreach (t ${targets})
+ add_executable (${t} ${t}.cpp)
+ target_link_libraries (${t} ${libraries})
+endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/triangle/triangle-chains.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,33 @@
+from dionysus import Simplex, Filtration, DynamicPersistenceChains, \
+ vertex_cmp, data_cmp, data_dim_cmp \
+
+complex = [Simplex((0,), 0), # A
+ Simplex((1,), 1), # B
+ Simplex((2,), 2), # C
+ Simplex((0,1), 2.5), # AB
+ Simplex((1,2), 2.9), # BC
+ Simplex((0,2), 3.5)] # CA
+
+print "Complex:", complex
+print "Vertex: ", sorted(complex, vertex_cmp)
+print "Data: ", sorted(complex, data_cmp)
+print "DataDim:", sorted(complex, data_dim_cmp)
+
+f = Filtration(complex, data_cmp)
+print "Complex in the filtration order:", ', '.join((str(s) for s in f))
+
+p = DynamicPersistenceChains(f)
+print "Persistence initialized"
+p.pair_simplices()
+print "Simplices paired"
+
+smap = p.make_simplex_map(f)
+for i in p:
+ print i.sign(), i.pair().sign()
+ print "%s (%d) - %s (%d)" % (smap[i], i.sign(), smap[i.pair()], i.pair().sign())
+ print "Cycle (%d):" % len(i.cycle), " + ".join((str(smap[ii]) for ii in i.cycle))
+
+ if i.unpaired():
+ print "Chain (%d):" % len(i.chain), " + ".join((str(smap[ii]) for ii in i.chain))
+
+print "Number of unpaired simplices:", len([i for i in p if i.unpaired()])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/triangle/triangle-zigzag.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,87 @@
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+#include <topology/simplex.h>
+#include <topology/zigzag-persistence.h>
+#include <boost/tuple/tuple.hpp>
+
+typedef ZigzagPersistence<unsigned> Zigzag;
+typedef Zigzag::SimplexIndex Index;
+typedef Zigzag::Death Death;
+typedef Zigzag::ZColumn Boundary;
+typedef std::vector<Index> Complex;
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ //stdoutLog.subscribeTo(RLOG_CHANNEL("topology/persistence"));
+#endif
+
+
+ Zigzag zz;
+ Complex c;
+ Index i; Death d;
+ unsigned birth = 0;
+
+ // Adding the triangle
+ std::cout << birth << ": adding 0" << std::endl;
+ boost::tie(i, d) = zz.add(Boundary(), birth++); // A
+ c.push_back(i);
+ assert(!d); // birth
+
+ std::cout << birth << ": adding 1" << std::endl;
+ boost::tie(i, d) = zz.add(Boundary(), birth++); // B
+ c.push_back(i);
+ assert(!d); // birth
+
+ std::cout << birth << ": adding 2" << std::endl;
+ boost::tie(i, d) = zz.add(Boundary(), birth++); // C
+ c.push_back(i);
+ assert(!d); // birth
+
+ std::cout << birth << ": adding 3" << std::endl;
+ boost::tie(i, d) = zz.add(Boundary(c.begin(),
+ boost::next(c.begin(),2)),
+ birth++); // AB
+ c.push_back(i);
+ assert(d); // death
+ if (d) std::cout << "Death of: " << *d << std::endl;
+
+ std::cout << birth << ": adding 4" << std::endl;
+ boost::tie(i, d) = zz.add(Boundary(boost::next(c.begin()),
+ boost::next(c.begin(),3)),
+ birth++); // BC
+ c.push_back(i);
+ assert(d); // death
+ if (d) std::cout << "Death of: " << *d << std::endl;
+
+ std::cout << birth << ": adding 5" << std::endl;
+ {
+ Boundary bdry; bdry.append(*c.begin(), zz.cmp); bdry.append(*boost::next(c.begin(), 2), zz.cmp);
+ boost::tie(i, d) = zz.add(bdry, birth++); // AC
+ }
+ c.push_back(i);
+ assert(!d); // birth
+
+ std::cout << birth << ": adding 6" << std::endl;
+ boost::tie(i, d) = zz.add(Boundary(boost::next(c.begin(), 3),
+ boost::next(c.begin(), 6)),
+ birth++); // ABC
+ c.push_back(i);
+ assert(d); // death
+ if (d) std::cout << "Death of: " << *d << std::endl;
+
+ //zz.show_all();
+
+ // Removing the triangle in reverse order
+ for (Complex::reverse_iterator cur = c.rbegin(); cur != c.rend(); ++cur)
+ {
+ std::cout << birth << ": removing " << (*cur)->order << std::endl;
+ d = zz.remove(*cur, birth++);
+ if (d) std::cout << "Death of: " << *d << std::endl;
+ //zz.show_all();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/triangle/triangle-zigzag.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,35 @@
+from dionysus import Simplex, ZigzagPersistence, \
+ vertex_cmp, data_cmp \
+# ,enable_log
+
+complex = {Simplex((0,), 0): None, # A
+ Simplex((1,), 1): None, # B
+ Simplex((2,), 2): None, # C
+ Simplex((0,1), 2.5): None, # AB
+ Simplex((1,2), 2.9): None, # BC
+ Simplex((0,2), 3.5): None, # CA
+ Simplex((0,1,2), 5): None} # ABC
+
+print "Complex:"
+for s in sorted(complex.keys()): print s
+print
+
+#enable_log("topology/persistence")
+zz = ZigzagPersistence()
+
+# Add all the simplices
+b = 1
+for s in sorted(complex.keys(), data_cmp):
+ print "%d: Adding %s" % (b, s)
+ i,d = zz.add([complex[ss] for ss in s.boundary], b)
+ complex[s] = i
+ if d: print "Interval (%d, %d)" % (d, b-1)
+ b += 1
+
+# Remove all the simplices
+for s in sorted(complex.keys(), data_cmp, reverse = True):
+ print "%d: Removing %s" % (b, s)
+ d = zz.remove(complex[s], b)
+ del complex[s]
+ if d: print "Interval (%d, %d)" % (d, b-1)
+ b += 1
--- a/examples/triangle/triangle.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/examples/triangle/triangle.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,63 +1,140 @@
-#include "topology/filtration.h"
+#include <utilities/log.h>
+
#include "topology/simplex.h"
+#include "topology/filtration.h"
+#include "topology/static-persistence.h"
+#include "topology/dynamic-persistence.h"
+#include "topology/persistence-diagram.h"
+#include <utilities/indirect.h>
+
#include <vector>
+#include <map>
#include <iostream>
-typedef SimplexWithValue<int> Simplex;
-typedef Filtration<Simplex> TriangleFiltration;
+
+#if 1
+#include <fstream>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/serialization/vector.hpp>
+#endif
-void fillTriangleSimplices(TriangleFiltration& f)
+typedef unsigned Vertex;
+typedef Simplex<Vertex, double> Smplx;
+typedef Filtration<Smplx> Fltr;
+// typedef StaticPersistence<> Persistence;
+typedef DynamicPersistenceTrails<> Persistence;
+typedef PersistenceDiagram<> PDgm;
+typedef OffsetBeginMap<Persistence, Fltr,
+ Persistence::iterator,
+ Fltr::Index> PersistenceFiltrationMap;
+typedef OffsetBeginMap<Fltr, Persistence,
+ Fltr::Index,
+ Persistence::iterator> FiltrationPersistenceMap;
+
+// Transposes elements of the filtration together with the
+struct FiltrationTranspositionVisitor: public Persistence::TranspositionVisitor
{
- typedef std::vector<int> VertexVector;
- VertexVector vertices(4);
- vertices[0] = 0; vertices[1] = 1; vertices[2] = 2;
- vertices[3] = 0;
-
- VertexVector::const_iterator bg = vertices.begin();
- VertexVector::const_iterator end = vertices.end();
- f.append(Simplex(bg, bg + 1, 0)); // 0 = A
- f.append(Simplex(bg + 1, bg + 2, 1)); // 1 = B
- f.append(Simplex(bg + 2, bg + 3, 2)); // 2 = C
- f.append(Simplex(bg, bg + 2, 2.5)); // AB
- f.append(Simplex(bg + 1, bg + 3, 2.9)); // BC
- f.append(Simplex(bg + 2, end, 3.5)); // CA
- f.append(Simplex(bg, bg + 3, 5)); // ABC
+ typedef Persistence::iterator iterator;
+
+ FiltrationTranspositionVisitor(const Persistence& p,
+ Fltr& f):
+ p_(p), f_(f) {}
+ void transpose(iterator i) { f_.transpose(f_.begin() + (i - p_.begin())); }
+
+ const Persistence& p_;
+ Fltr& f_;
+};
+
+void fillTriangleSimplices(Fltr& c)
+{
+ typedef std::vector<Vertex> VertexVector;
+ VertexVector vertices(4);
+ vertices[0] = 0; vertices[1] = 1; vertices[2] = 2;
+ vertices[3] = 0;
+
+ VertexVector::const_iterator bg = vertices.begin();
+ VertexVector::const_iterator end = vertices.end();
+ c.push_back(Smplx(bg, bg + 1, 0)); // 0 = A
+ c.push_back(Smplx(bg + 1, bg + 2, 1)); // 1 = B
+ c.push_back(Smplx(bg + 2, bg + 3, 2)); // 2 = C
+ c.push_back(Smplx(bg, bg + 2, 2.5)); // AB
+ c.push_back(Smplx(bg + 1, bg + 3, 2.9)); // BC
+ c.push_back(Smplx(bg + 2, end, 3.5)); // CA
+ c.push_back(Smplx(bg, bg + 3, 5)); // ABC
}
-int main()
+int main(int argc, char** argv)
{
-#ifdef CWDEBUG
- dionysus::debug::init();
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stdoutLog.subscribeTo(RLOG_CHANNEL("topology/persistence"));
+ //stdoutLog.subscribeTo(RLOG_CHANNEL("topology/chain"));
+ //stdoutLog.subscribeTo(RLOG_CHANNEL("topology/vineyard"));
+#endif
- Debug(dc::filtration.on());
- Debug(dc::cycle.off());
- Debug(dc::vineyard.on());
- Debug(dc::transpositions.on());
+ Fltr f;
+ fillTriangleSimplices(f);
+ std::cout << "Simplices filled" << std::endl;
+ for (Fltr::Index cur = f.begin(); cur != f.end(); ++cur)
+ std::cout << " " << *cur << std::endl;
+
+#if 1 // testing serialization of the Filtration (really Simplex)
+ {
+ std::ofstream ofs("complex");
+ boost::archive::text_oarchive oa(ofs);
+ oa << f;
+ f.clear();
+ }
+
+ {
+ std::ifstream ifs("complex");
+ boost::archive::text_iarchive ia(ifs);
+ ia >> f;
+ }
#endif
- Evaluator<Simplex> e;
- TriangleFiltration::Vineyard v(&e);
- TriangleFiltration tf(&v);
- fillTriangleSimplices(tf);
-
- tf.fill_simplex_index_map();
- tf.pair_simplices();
- v.start_vines(tf.begin(), tf.end());
-
- std::cout << "Filtration size: " << tf.size() << std::endl;
- std::cout << tf << std::endl;
+ f.sort(Smplx::DataComparison());
+ std::cout << "Filtration initialized" << std::endl;
+ std::cout << f << std::endl;
+
+ Persistence p(f);
+ std::cout << "Persistence initialized" << std::endl;
+
+ p.pair_simplices();
+ std::cout << "Simplices paired" << std::endl;
+
+ Persistence::SimplexMap<Fltr> m = p.make_simplex_map(f);
+ std::map<Dimension, PDgm> dgms;
+ init_diagrams(dgms, p.begin(), p.end(),
+ evaluate_through_map(m, Smplx::DataEvaluator()),
+ evaluate_through_map(m, Smplx::DimensionExtractor()));
+
+ std::cout << 0 << std::endl << dgms[0] << std::endl;
+ std::cout << 1 << std::endl << dgms[1] << std::endl;
-#if 1
- Simplex BC; BC.add(1); BC.add(2);
- Simplex AB; AB.add(0); AB.add(1);
- std::cout << BC << std::endl;
- std::cout << *tf.get_index(BC) << std::endl;
- tf.transpose(tf.get_index(BC));
- std::cout << tf;
- std::cout << AB << std::endl;
- std::cout << *tf.get_index(AB) << std::endl;
- tf.transpose(tf.get_index(AB));
- std::cout << tf;
-#endif
+ PersistenceFiltrationMap pfmap(p, f);
+ DimensionFunctor<PersistenceFiltrationMap, Fltr> dim(pfmap, f);
+
+ // Transpositions
+ FiltrationPersistenceMap fpmap(f, p);
+ FiltrationTranspositionVisitor visitor(p, f);
+ Smplx A; A.add(0);
+ std::cout << A << std::endl;
+ std::cout << "Transposing A: " << p.transpose(fpmap[f.find(A)], dim, visitor) << std::endl; // 1.2 unpaired
+
+ Smplx BC; BC.add(1); BC.add(2);
+ Smplx AB; AB.add(0); AB.add(1);
+ std::cout << BC << std::endl;
+ std::cout << p.transpose(fpmap[f.find(BC)], dim, visitor) << std::endl; // 3.1
+ // p.transpose(fpmap[f.find(BC)], dim, visitor);
+ std::cout << AB << std::endl;
+ std::cout << p.transpose(fpmap[f.find(AB)], dim, visitor) << std::endl; // 2.1
+ // p.transpose(fpmap[f.find(AB)], dim, visitor);
+
+ std::cout << p.transpose(p.begin(), dim, visitor) << std::endl; // transposition case 1.2 special
+ std::cout << p.transpose(boost::next(p.begin()), dim, visitor) << std::endl;
+ std::cout << p.transpose(boost::next(p.begin(),3), dim, visitor) << std::endl;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/triangle/triangle.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,31 @@
+from dionysus import Simplex, Filtration, StaticPersistence, \
+ vertex_cmp, data_cmp, data_dim_cmp \
+
+complex = [Simplex((0,), 0), # A
+ Simplex((1,), 1), # B
+ Simplex((2,), 2), # C
+ Simplex((0,1), 2.5), # AB
+ Simplex((1,2), 2.9), # BC
+ Simplex((0,2), 3.5), # CA
+ Simplex((0,1,2), 5)] # ABC
+
+print "Complex:", complex
+print "Vertex: ", sorted(complex, vertex_cmp)
+print "Data: ", sorted(complex, data_cmp)
+print "DataDim:", sorted(complex, data_dim_cmp)
+
+f = Filtration(complex, data_cmp)
+print "Complex in the filtration order:", ', '.join((str(s) for s in f))
+
+p = StaticPersistence(f)
+print "Persistence initialized"
+p.pair_simplices(True)
+print "Simplices paired"
+
+smap = p.make_simplex_map(f)
+for i in p:
+ print i.sign(), i.pair().sign()
+ print "%s (%d) - %s (%d)" % (smap[i], i.sign(), smap[i.pair()], i.pair().sign())
+ print "Cycle (%d):" % len(i.cycle), " + ".join((str(smap[ii]) for ii in i.cycle))
+
+print "Number of unpaired simplices:", len([i for i in p if i.unpaired()])
--- a/include/dionysus.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/dionysus.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,6 +1,6 @@
/*
* Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2006
+ * Department of Computer Science, Duke University, 2006--2008
*
* For now file exists only to store the main page of the documentation
*/
@@ -9,3 +9,16 @@
* Detailed description of Dionysus with references to all the classes,
* and code samples goes here.
*/
+
+/**
+ * \defgroup topology Topology Classes
+ */
+
+/**
+ * \defgroup geometry Geometry Classes
+ */
+
+/**
+ * \defgroup kinetic Kinetic Data Structures Classes
+ * \ingroup geometry
+ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/geometry/distances.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,71 @@
+#ifndef __DISTANCES_H__
+#define __DISTANCES_H__
+
+#include <vector>
+
+/**
+ * Class: ExplicitDistances
+ * Stores the pairwise distances of Distances_ instance passed at construction.
+ * It's a protypical Distances template argument for the Rips complex.
+ */
+template<class Distances_>
+class ExplicitDistances
+{
+ public:
+ typedef Distances_ Distances;
+ typedef size_t IndexType;
+ typedef typename Distances::DistanceType DistanceType;
+
+ ExplicitDistances(IndexType size):
+ size_(size),
+ distances_(size*(size + 1)/2 + size) {}
+ ExplicitDistances(const Distances& distances);
+
+ DistanceType operator()(IndexType a, IndexType b) const;
+ DistanceType& operator()(IndexType a, IndexType b);
+
+ size_t size() const { return size_; }
+ IndexType begin() const { return 0; }
+ IndexType end() const { return size(); }
+
+ private:
+ std::vector<DistanceType> distances_;
+ size_t size_;
+};
+
+
+/**
+ * Class: PairwiseDistances
+ * Given a Container_ of points and a Distance_, it computes distances between elements
+ * in the container (given as instances of Index_ defaulted to unsigned) using the Distance_ functor.
+ *
+ * Container_ is assumed to be an std::vector. That simplifies a number of things.
+ */
+template<class Container_, class Distance_, typename Index_ = unsigned>
+class PairwiseDistances
+{
+ public:
+ typedef Container_ Container;
+ typedef Distance_ Distance;
+ typedef Index_ IndexType;
+ typedef typename Distance::result_type DistanceType;
+
+
+ PairwiseDistances(const Container& container,
+ const Distance& distance = Distance()):
+ container_(container), distance_(distance) {}
+
+ DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); }
+
+ size_t size() const { return container_.size(); }
+ IndexType begin() const { return 0; }
+ IndexType end() const { return size(); }
+
+ private:
+ const Container& container_;
+ Distance distance_;
+};
+
+#include "distances.hpp"
+
+#endif // __DISTANCES_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/geometry/distances.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,30 @@
+template<class Distances_>
+ExplicitDistances<Distances_>::
+ExplicitDistances(const Distances& distances):
+ size_(distances.size()), distances_((distances.size() * (distances.size() + 1))/2)
+{
+ IndexType i = 0;
+ for (typename Distances::IndexType a = distances.begin(); a != distances.end(); ++a)
+ for (typename Distances::IndexType b = a; b != distances.end(); ++b)
+ {
+ distances_[i++] = distances(a,b);
+ }
+}
+
+template<class Distances_>
+typename ExplicitDistances<Distances_>::DistanceType
+ExplicitDistances<Distances_>::
+operator()(IndexType a, IndexType b) const
+{
+ if (a > b) std::swap(a,b);
+ return distances_[a*size_ - ((a*(a-1))/2) + (b-a)];
+}
+
+template<class Distances_>
+typename ExplicitDistances<Distances_>::DistanceType&
+ExplicitDistances<Distances_>::
+operator()(IndexType a, IndexType b)
+{
+ if (a > b) std::swap(a,b);
+ return distances_[a*size_ - ((a*(a-1))/2) + (b-a)];
+}
--- a/include/geometry/euclidean.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/geometry/euclidean.h Tue Jun 27 09:37:05 2017 -0700
@@ -14,15 +14,19 @@
#include "number-traits.h"
+/**
+ * Geometric Kernel. Defines operations on geometric primitives.
+ * \ingroup geometry
+ */
template<class NumberType_ = double>
class Kernel
{
public:
typedef unsigned int DimensionType;
typedef NumberType_ NumberType;
- typedef LinearAlgebra<NumberType> LinearAlgebra;
- typedef typename LinearAlgebra::MatrixType MatrixType;
- typedef typename LinearAlgebra::VectorType VectorType;
+ typedef LinearAlgebra<NumberType> LinearAlgebraK;
+ typedef typename LinearAlgebraK::MatrixType MatrixType;
+ typedef typename LinearAlgebraK::VectorType VectorType;
class Point;
class Sphere;
@@ -56,7 +60,10 @@
};
-/* Point */
+/**
+ * Point class.
+ * \ingroup geometry
+ */
template<class NumberType_>
class Kernel<NumberType_>::Point: public VectorType
{
@@ -77,7 +84,10 @@
};
-/* Sphere */
+/**
+ * Sphere class.
+ * \ingroup geometry
+ */
template<class NumberType_>
class Kernel<NumberType_>::Sphere
{
--- a/include/geometry/kinetic-sort.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/geometry/kinetic-sort.h Tue Jun 27 09:37:05 2017 -0700
@@ -7,38 +7,40 @@
#include <iostream>
/**
- * Maintains elements of the given data structure in the sorted order assuming the elements follow
+ * Maintains elements of the given data structure in the sorted order assuming the elements follow
* trajectories given by TrajectoryExtractor_.
*
* \arg ElementIterator_ iterator over the underlying data structure that's kept in sorted order
- * \arg TrajectoryExtractor_ applied to the iterator into SortDS_ should return a rational
- * function describing the
- * \arg Simulator_ the Simulator type, e.g. Simulator. Note that KineticSort does not store
+ * \arg TrajectoryExtractor_ applied to the iterator into SortDS_ should return a function
+ * (of type Simulator_::FunctionKernel::Function) describing the trajectory of the element
+ * \arg Simulator_ the Simulator type, e.g. Simulator. Note that KineticSort does not store
* a pointer to the Simulator (so a pointer is passed in each relevant operation)
* \arg Swap_ is called with an ElementIterator_ when a swap needs to be performed
+ *
+ * \ingroup kinetic
*/
-template<class ElementIterator_, class TrajectoryExtractor_,
+template<class ElementIterator_, class TrajectoryExtractor_,
class Simulator_, class Swap_ = boost::function<void(ElementIterator_ pos, Simulator_* simulator)> >
class KineticSort
{
public:
typedef Simulator_ Simulator;
- typedef typename Simulator::PolynomialKernel PolynomialKernel;
+ typedef typename Simulator::FunctionKernel FunctionKernel;
typedef ElementIterator_ ElementIterator;
typedef Swap_ Swap;
typedef TrajectoryExtractor_ TrajectoryExtractor;
-
+
typedef typename Simulator::Key SimulatorKey;
-
+
private:
/* Implementation */
struct Node
{
ElementIterator element;
- SimulatorKey swap_event_key;
+ SimulatorKey swap_event_key;
- Node(ElementIterator e, SimulatorKey k):
+ Node(ElementIterator e, SimulatorKey k):
element(e), swap_event_key(k) {}
};
@@ -51,7 +53,7 @@
/// \name Core Functionality
/// @{
KineticSort();
- KineticSort(ElementIterator b, ElementIterator e, Swap swap, Simulator* simulator);
+ KineticSort(ElementIterator b, ElementIterator e, Swap swap, Simulator* simulator, const TrajectoryExtractor& te = TrajectoryExtractor());
void initialize(ElementIterator b, ElementIterator e, Swap swap, Simulator* simulator);
void insert(iterator pos, ElementIterator f, ElementIterator l, Simulator* simulator);
@@ -66,6 +68,8 @@
iterator begin() { return list_.begin(); }
iterator end() { return list_.end(); }
+ const TrajectoryExtractor& trajectory_extractor() const { return te_; }
+
private:
class SwapEvent;
void schedule_swaps(iterator b, iterator e, Simulator* s);
@@ -73,7 +77,8 @@
private:
NodeList list_;
- Swap swap_;
+ Swap swap_;
+ TrajectoryExtractor te_;
};
#include "kinetic-sort.hpp"
--- a/include/geometry/kinetic-sort.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/include/geometry/kinetic-sort.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,3 +1,19 @@
+#include "utilities/log.h"
+#include "utilities/counter.h"
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlKineticSort = DEF_CHANNEL("geometry/kinetic-sort", rlog::Log_Debug);
+static rlog::RLogChannel* rlKineticSortAudit = DEF_CHANNEL("geometry/kinetic-sort/audit", rlog::Log_Debug);
+static rlog::RLogChannel* rlKineticSortSchedule = DEF_CHANNEL("geometry/kinetic-sort/schedule", rlog::Log_Debug);
+static rlog::RLogChannel* rlKineticSortProcess = DEF_CHANNEL("geometry/kinetic-sort/process", rlog::Log_Debug);
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cKineticSort = GetCounter("kinetic-sort");
+static Counter* cKineticSortSwap = GetCounter("kinetic-sort/swap");
+#endif // COUNTERS
+
+
template<class ElementIterator_, class TrajectoryExtractor_, class Simulator_, class Swap_>
KineticSort<ElementIterator_, TrajectoryExtractor_, Simulator_, Swap_>::
KineticSort()
@@ -5,7 +21,8 @@
template<class ElementIterator_, class TrajectoryExtractor_, class Simulator_, class Swap_>
KineticSort<ElementIterator_, TrajectoryExtractor_, Simulator_, Swap_>::
-KineticSort(ElementIterator b, ElementIterator e, Swap swap, Simulator* simulator)
+KineticSort(ElementIterator b, ElementIterator e, Swap swap, Simulator* simulator, const TrajectoryExtractor& te):
+ te_(te)
{
initialize(b, e, swap, simulator);
}
@@ -81,6 +98,7 @@
{
// TODO: AssertMsg(boost::next(pos) != list_.end(), "Cannot swap the last element");
+ Count(cKineticSortSwap);
swap_(pos->element, simulator);
// Remove events
@@ -106,35 +124,36 @@
KineticSort<ElementIterator_, TrajectoryExtractor_, Simulator_, Swap_>::
audit(Simulator* simulator) const
{
- typedef typename Simulator::RationalFunction RationalFunction;
+ typedef typename Simulator::Function Function;
typedef typename Simulator::Time Time;
Time t = simulator->audit_time();
- std::cout << "Auditing at " << t << std::endl;
+ rLog(rlKineticSortAudit, "Auditing at %s", tostring(t).c_str());
- TrajectoryExtractor te;
-
typename NodeList::const_iterator next = list_.begin();
typename NodeList::const_iterator cur = next++;
- RationalFunction cur_trajectory = te(cur->element);
+ Function cur_trajectory = te_(cur->element);
while (next != list_.end())
{
- (*(cur->swap_event_key))->print(std::cout << " ") << std::endl;
+ rLog(rlKineticSortAudit, " %s", intostring(**(cur->swap_event_key)).c_str());
- RationalFunction next_trajectory = te(next->element);
- std::cout << " Auditing: " << cur_trajectory << ", " << next_trajectory << std::endl;
- std::cout << " Difference: " << next_trajectory - cur_trajectory << std::endl;
- std::cout << " Sign at: " << t << ", " << PolynomialKernel::sign_at(next_trajectory - cur_trajectory, t) << std::endl;
- if (PolynomialKernel::sign_at(next_trajectory - cur_trajectory, t) == -1)
+ Function next_trajectory = te_(next->element);
+ rLog(rlKineticSortAudit, " Auditing: %s, %s", tostring(cur_trajectory).c_str(),
+ tostring(next_trajectory).c_str());
+ rLog(rlKineticSortAudit, " Difference: %s", tostring(next_trajectory - cur_trajectory).c_str());
+ rLog(rlKineticSortAudit, " Sign at: %s, %s", tostring(t).c_str(),
+ tostring(FunctionKernel::sign_at(next_trajectory - cur_trajectory, t)).c_str());
+ if (FunctionKernel::sign_at(next_trajectory - cur_trajectory, t) == -1)
{
- std::cout << "Audit failed at " << *cur->element << ", " << *next->element << std::endl;
+ // rError("Audit failed at %s, %s", tostring(*cur->element).c_str(),
+ // tostring(*next->element).c_str());
return false;
}
cur_trajectory = next_trajectory;
cur = next++;
}
- if (cur != list_.end()) (*(cur->swap_event_key))->print(std::cout << " ") << std::endl;
+ if (cur != list_.end()) rLog(rlKineticSortAudit, " %s", intostring(**(cur->swap_event_key)).c_str());
return true;
}
@@ -144,17 +163,15 @@
KineticSort<ElementIterator_, TrajectoryExtractor_, Simulator_, Swap_>::
schedule_swaps(iterator b, iterator e, Simulator* simulator)
{
- typedef typename Simulator::RationalFunction RationalFunction;
-
- TrajectoryExtractor te;
+ typedef typename Simulator::Function Function;
iterator next = b;
iterator cur = next++;
- RationalFunction cur_trajectory = te(cur->element);
+ Function cur_trajectory = te_(cur->element);
while (next != e)
{
- RationalFunction next_trajectory = te(next->element);
- std::cout << "Next trajectory: " << next_trajectory << std::endl;
+ Function next_trajectory = te_(next->element);
+ rLog(rlKineticSortSchedule, "Next trajectory: %s", tostring(next_trajectory).c_str());
// TODO: add assertion that (next_trajectory - cur_trajectory)(s->curren_time()) > 0
cur->swap_event_key = simulator->add(next_trajectory - cur_trajectory, SwapEvent(this, cur));
cur = next++;
@@ -168,7 +185,7 @@
KineticSort<ElementIterator_, TrajectoryExtractor_, Simulator_, Swap_>::
schedule_swaps(iterator i, Simulator* simulator)
{
- typedef typename Simulator::RationalFunction RationalFunction;
+ typedef typename Simulator::Function Function;
if (i == list_.end()) return;
if (boost::next(i) == list_.end())
@@ -177,17 +194,14 @@
return;
}
- TrajectoryExtractor te;
-
iterator next = boost::next(i);
- RationalFunction i_trajectory = te(i->element);
- RationalFunction next_trajectory = te(next->element);
+ Function i_trajectory = te_(i->element);
+ Function next_trajectory = te_(next->element);
//std::cout << "Updating swaps for: " << i_trajectory << ", " << next_trajectory << std::endl;
//std::cout << "Difference: " << next_trajectory - i_trajectory << std::endl;
i->swap_event_key = simulator->add(next_trajectory - i_trajectory, SwapEvent(this, i));
- //i->swap_event_key = simulator->add(next_trajectory, SwapEvent(this, i));
}
/* SwapEvent */
@@ -205,7 +219,7 @@
virtual bool process(Simulator* s) const;
void set_position(iterator i) { pos_ = i; }
iterator position() const { return pos_; }
- std::ostream& print(std::ostream& out) const;
+ std::ostream& operator<<(std::ostream& out) const;
private:
KineticSort* sort_;
@@ -217,7 +231,7 @@
KineticSort<ElementIterator_, TrajectoryExtractor_, Simulator_, Swap_>::SwapEvent::
process(Simulator* s) const
{
- std::cout << "Swapping. Current time: " << s->current_time() << std::endl;
+ rLog(rlKineticSortProcess, "Swapping. Current time: %s", tostring(s->current_time()).c_str());
sort_->swap(pos_, s);
return true;
}
@@ -225,9 +239,8 @@
template<class ElementIterator_, class TrajectoryExtractor_, class Simulator_, class Swap_>
std::ostream&
KineticSort<ElementIterator_, TrajectoryExtractor_, Simulator_, Swap_>::SwapEvent::
-print(std::ostream& out) const
+operator<<(std::ostream& out) const
{
- Parent::print(out) << ", SwapEvent at " << TrajectoryExtractor_()(position()->element);
+ Parent::operator<<(out) << "SwapEvent at " << sort_->trajectory_extractor()(position()->element);
return out;
}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/geometry/l2distance.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,47 @@
+#ifndef __L2_DISTANCE_H__
+#define __L2_DISTANCE_H__
+
+#include <utilities/types.h>
+#include <utilities/log.h>
+
+#include <vector>
+#include <fstream>
+#include <functional>
+#include <cmath>
+#include <string>
+#include <sstream>
+
+
+typedef std::vector<double> Point;
+typedef std::vector<Point> PointContainer;
+
+struct L2Distance:
+ public std::binary_function<const Point&, const Point&, double>
+{
+ result_type operator()(const Point& p1, const Point& p2) const
+ {
+ AssertMsg(p1.size() == p2.size(), "Points must be in the same dimension (in L2Distance): dim1=%d, dim2=%d", p1.size(), p2.size());
+ result_type sum = 0;
+ for (size_t i = 0; i < p1.size(); ++i)
+ sum += (p1[i] - p2[i])*(p1[i] - p2[i]);
+
+ return sqrt(sum);
+ }
+};
+
+void read_points(const std::string& infilename, PointContainer& points)
+{
+ std::ifstream in(infilename.c_str());
+ std::string line;
+ while(std::getline(in, line))
+ {
+ if (line[0] == '#') continue; // comment line in the file
+ std::stringstream linestream(line);
+ double x;
+ points.push_back(Point());
+ while (linestream >> x)
+ points.back().push_back(x);
+ }
+}
+
+#endif // __L2_DISTANCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/geometry/linear-kernel.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,46 @@
+#ifndef __LINEAR_KERNEL_H__
+#define __LINEAR_KERNEL_H__
+
+#include <stack>
+#include <iostream>
+#include <boost/operators.hpp>
+
+template<class T>
+class LinearKernel
+{
+ public:
+ struct Function;
+
+ typedef T RootType;
+ typedef std::stack<RootType> RootStack;
+
+ static void solve(const Function& f, RootStack& stack) { if (f.a1 != 0) stack.push(-f.a0/f.a1); }
+ static RootType root(const T& r) { return r; }
+ static int sign_at(const Function& f, const RootType& r) { RootType y = f(r); if (y < 0) return -1; if (y > 0) return 1; return 0; }
+ static RootType between(const RootType& r1, const RootType& r2) { return (r1 + r2)/2; }
+ static int sign_at_negative_infinity(const Function& f) { if (f.a1 < 0) return 1; if (f.a1 > 0) return -1; if (f.a0 > 0) return 1; if (f.a0 < 0) return -1; return 0; }
+};
+
+template<class T>
+struct LinearKernel<T>::Function: boost::additive<Function>
+{
+ typedef T RootType;
+
+ Function(RootType aa0, RootType aa1 = 0):
+ a0(aa0), a1(aa1) {}
+
+ RootType operator()(RootType r) const { return a1 * r + a0; }
+ Function& operator+=(const Function& other) { a1 += other.a1; a0 += other.a0; return *this; }
+ Function& operator-=(const Function& other) { a1 -= other.a1; a0 -= other.a0; return *this; }
+ std::ostream& operator<<(std::ostream& out) const { out << a1 << "*x + " << a0; return out; }
+
+ RootType a0, a1;
+};
+
+// TODO: need to make this generic
+std::ostream& operator<<(std::ostream& out, const LinearKernel<double>::Function& f)
+{
+ return f.operator<<(out);
+}
+
+#endif
--- a/include/geometry/polynomial.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/geometry/polynomial.h Tue Jun 27 09:37:05 2017 -0700
@@ -21,17 +21,18 @@
{
public:
typedef typename SynapsTraits<T>::Polynomial Polynomial;
- typedef RationalFunction<Polynomial> RationalFunction;
+ typedef RationalFunction<Polynomial> RationalFunctionP;
+ typedef RationalFunctionP Function;
typedef typename SynapsTraits<T>::Solver Solver;
typedef typename SynapsTraits<T>::RootType RootType;
typedef std::stack<RootType> RootStack;
- static void solve(const RationalFunction& rf, RootStack& stack);
+ static void solve(const RationalFunctionP& rf, RootStack& stack);
static RootType root(const T& r) { return SynapsTraits<T>::root(r); }
- static int sign_at(const RationalFunction& rf, const RootType& r);
+ static int sign_at(const RationalFunctionP& rf, const RootType& r);
static RootType between(const RootType& r1, const RootType& r2) { return SynapsTraits<T>::between(r1,r2); }
- static bool sign_at_negative_infinity(const RationalFunction& rf);
+ static int sign_at_negative_infinity(const RationalFunctionP& rf);
};
template<class T>
--- a/include/geometry/polynomial.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/include/geometry/polynomial.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,10 +1,14 @@
+#include <utilities/log.h>
+
template<class T>
void
UPolynomial<T>::
-solve(const RationalFunction& rf, RootStack& stack)
+solve(const RationalFunctionP& rf, RootStack& stack)
{
typedef SYNAPS::Seq<RootType> RootSeq;
+ AssertMsg(stack.empty(), "Stack must be empty before solve");
+
RootSeq seq_num = SYNAPS::solve(SynapsTraits<T>::convert(rf.numerator()), Solver());
RootSeq seq_den = SYNAPS::solve(SynapsTraits<T>::convert(rf.denominator()), Solver());
@@ -28,22 +32,23 @@
template<class T>
int
UPolynomial<T>::
-sign_at(const RationalFunction& rf, const RootType& r)
+sign_at(const RationalFunctionP& rf, const RootType& r)
{
return SynapsTraits<T>::sign_at(rf.numerator(), r) * SynapsTraits<T>::sign_at(rf.denominator(), r);
}
template<class T>
-bool
+int
UPolynomial<T>::
-sign_at_negative_infinity(const RationalFunction& rf)
+sign_at_negative_infinity(const RationalFunctionP& rf)
{
const Polynomial& num = rf.numerator();
const Polynomial& den = rf.denominator();
- unsigned int ndegree = num.get_degree();
- unsigned int ddegree = den.get_degree();
- return (((ndegree + 1) % 2 == 0) ^ (num[ndegree] > 0)) ^
- (((ddegree + 1) % 2 == 0) ^ (den[ddegree] > 0));
+ int ndegree = num.get_degree();
+ int ddegree = den.get_degree();
+ if (ndegree == -1) return 0; // ndegree == -1 => num == 0, and 0 is 0 at -infinity
+ return !((((ndegree + 1) % 2 == 0) ^ (num[ndegree] > 0)) ^
+ (((ddegree + 1) % 2 == 0) ^ (den[ddegree] > 0))) ? 1:-1;
}
SynapsTraits<QQ>::RootType
--- a/include/geometry/simulator.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/geometry/simulator.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,121 +1,136 @@
#ifndef __SIMULATOR_H__
#define __SIMULATOR_H__
-#include "utilities/eventqueue.h"
-#include "polynomial.h"
+#include <utilities/eventqueue.h>
+#include <utilities/indirect.h>
-template<class Comparison> class IndirectComparison;
+#include <limits>
/**
- * Simulator class. Keeps a queue of events. Infinity is reached if the Event
- * at the front of the queue has an empty root stack. Keeps track of current time,
- * Event addition, and processes events one by one. Degeneracies are handled by
- * assuming that the RationalFunction responsible for the event must be positive
- * before the Event occurs.
+ * Simulator class. Keeps a queue of events. Infinity is reached if the Event
+ * at the front of the queue has an empty root stack. Keeps track of current time,
+ * Event addition, and processes events one by one. Degeneracies are handled by
+ * assuming that the FunctionKernel::Function responsible for the event must be
+ * positive before the Event occurs.
+ *
+ * \ingroup kinetic
*/
-template<class PolyKernel_, template<class Event> class EventComparison_ = std::less>
+template<class FuncKernel_, template<class Event> class EventComparison_ = std::less>
class Simulator
{
- public:
- typedef PolyKernel_ PolynomialKernel;
- typedef typename PolynomialKernel::Polynomial Polynomial;
- typedef typename PolynomialKernel::RationalFunction RationalFunction;
- typedef typename PolynomialKernel::RootStack RootStack;
- typedef typename PolynomialKernel::RootType RootType;
- typedef RootType Time;
+ public:
+ typedef FuncKernel_ FunctionKernel;
+ typedef typename FunctionKernel::Function Function;
+ typedef typename FunctionKernel::RootStack RootStack;
+ typedef typename FunctionKernel::RootType RootType;
+ typedef RootType Time;
- class Event;
- typedef EventComparison_<Event> EventComparison;
- typedef EventQueue<Event*, IndirectComparison<EventComparison> >
- EventQueue;
- typedef typename EventQueue::iterator Key;
- typedef typename EventQueue::const_iterator const_Key;
+ class Event;
+ typedef EventComparison_<Event> EventComparison;
+
+ class IndirectEventComparison;
+ typedef EventQueue<Event*, IndirectEventComparison> EventQueueS;
+ typedef typename EventQueueS::iterator Key;
+ typedef typename EventQueueS::const_iterator const_Key;
- Simulator(Time start = PolynomialKernel::root(0)):
- current_(start),
- reached_infinity_(false) {}
+ Simulator(Time start = FunctionKernel::root(0)):
+ current_(start) {}
+ ~Simulator() { for (Key cur = queue_.top(); cur != queue_.end(); ++cur) delete *cur; }
- template<class Event_>
- Key add(const Event_& e);
- template<class Event_>
- Key add(const RationalFunction& f, const Event_& e);
- void process();
- void update(Key k, const RationalFunction& f);
-
- void remove(Key k) { queue_.remove(k); }
- Key null_key() { return queue_.end(); }
+ template<class Event_>
+ Key add(const Event_& e);
+ template<class Event_>
+ Key add(const Function& f, const Event_& e);
+ void process();
+ //void update(Key k, const Function& f);
+
+ void remove(Key k) { Event* e = *k; queue_.remove(k); delete e; }
+ Key null_key() { return queue_.end(); }
- Time current_time() const { return current_; }
- Time audit_time() const;
- bool reached_infinity() const { return reached_infinity_; }
-
- std::ostream& print(std::ostream& out) const;
+ Time current_time() const { return current_; }
+ Time audit_time() const;
+ bool reached_infinity() const { return queue_.empty() || (*queue_.top())->root_stack().empty(); }
+ Time next_event_time() const { return queue_.empty() ? std::numeric_limits<Time>::infinity():(*queue_.top())->root_stack().top(); }
- private:
- void update(Key i);
+ Event* top() const { return *(queue_.top()); }
+ unsigned size() const { return queue_.size(); }
+ unsigned event_count() const { return count_; }
- private:
- Time current_;
- EventQueue queue_;
- bool reached_infinity_;
+ std::ostream& operator<<(std::ostream& out) const;
+
+ private:
+ Time current_;
+ EventQueueS queue_;
+ unsigned count_;
};
/**
- * Base class for events. Stores a root stack, subclasses need to define process().
- * Event with an empty root stack compares greater than any other Event,
+ * Base class for events. Stores a root stack, subclasses need to define process().
+ * Event with an empty root stack compares greater than any other Event,
* pushing those events to the end of the queue.
*/
-template<class PolyKernel_, template<class Event> class EventComparison_>
-class Simulator<PolyKernel_, EventComparison_>::Event
+template<class FuncKernel_, template<class Event> class EventComparison_>
+class Simulator<FuncKernel_, EventComparison_>::Event
{
- public:
- typedef PolyKernel_ PolynomialKernel;
- typedef typename PolynomialKernel::RootStack RootStack;
+ public:
+ typedef FuncKernel_ FunctionKernel;
+ typedef typename FunctionKernel::RootStack RootStack;
- /// Returns true if the event needs to remain in the Simulator
- /// (top of the root_stack() will be used for new time)
- virtual bool process(Simulator* s) const =0;
-
- RootStack& root_stack() { return root_stack_; }
- const RootStack& root_stack() const { return root_stack_; }
+ virtual ~Event() {}
+
+ /// process() is called when the event is at the top of the queue
+ /// in the simulator.
+ /// Returns true if the event needs to remain in the Simulator
+ /// (top of the root_stack() will be used for new time).
+ virtual bool process(Simulator* s) const =0;
+ RootStack& root_stack() { return root_stack_; }
+ const RootStack& root_stack() const { return root_stack_; }
- bool operator<(const Event& e) const
- {
- if (root_stack().empty())
- return false;
- else if (e.root_stack().empty())
- return true;
- else
- return root_stack().top() < e.root_stack().top();
- }
+ bool operator<(const Event& e) const
+ {
+ if (root_stack().empty())
+ return false;
+ else if (e.root_stack().empty())
+ return true;
+ else
+ return root_stack().top() < e.root_stack().top();
+ }
- virtual std::ostream& print(std::ostream& out) const { return out << "Event with " << root_stack_.size() << " roots"; }
+ virtual std::ostream& operator<<(std::ostream& out) const
+ {
+ out << "Event with " << root_stack_.size() << " roots";
+ if (!root_stack_.empty()) out << "; top root: " << root_stack_.top();
+ out << ", ";
+ return out;
+ }
- private:
- RootStack root_stack_;
+ private:
+ RootStack root_stack_;
};
/**
- * Compares elements pointed at by its arguments using the provided Comparison_
+ * Compares elements pointed at by its arguments using the provided Comparison_
* (which must not take any arguments during construction).
*/
-template<class Comparison_>
-class IndirectComparison: public std::binary_function<const typename Comparison_::first_argument_type*,
- const typename Comparison_::second_argument_type*,
- bool>
+template<class FuncKernel_, template<class Event> class EventComparison_>
+class Simulator<FuncKernel_, EventComparison_>::IndirectEventComparison:
+ public std::binary_function<const typename EventComparison::first_argument_type*,
+ const typename EventComparison::second_argument_type*,
+ bool>
{
- public:
- typedef Comparison_ Comparison;
- typedef const typename Comparison::first_argument_type* first_argument_type;
- typedef const typename Comparison::second_argument_type* second_argument_type;
+ public:
+ typedef EventComparison Comparison;
+ typedef const typename Comparison::first_argument_type* first_argument_type;
+ typedef const typename Comparison::second_argument_type* second_argument_type;
- bool operator()(first_argument_type e1, second_argument_type e2) const
- { return Comparison()(*e1, *e2); }
+ bool operator()(first_argument_type e1, second_argument_type e2) const
+ { return Comparison()(*e1, *e2); }
};
+
#include "simulator.hpp"
#endif // __SIMULATOR_H__
--- a/include/geometry/simulator.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/include/geometry/simulator.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,87 +1,137 @@
-template<class PolyKernel_, template<class Event> class EventComparison_>
+#include "utilities/log.h"
+#include "utilities/counter.h"
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlSimulator = DEF_CHANNEL("geometry/simulator", rlog::Log_Debug);
+
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cSimulatorProcess = GetCounter("simulator/process");
+#endif // COUNTERS
+
+
+template<class FuncKernel_, template<class Event> class EventComparison_>
template<class Event_>
-typename Simulator<PolyKernel_, EventComparison_>::Key
-Simulator<PolyKernel_, EventComparison_>::
+typename Simulator<FuncKernel_, EventComparison_>::Key
+Simulator<FuncKernel_, EventComparison_>::
add(const Event_& e)
{
- Event* ee = new Event_(e);
- return queue_.push(ee);
+ Event* ee = new Event_(e);
+ return queue_.push(ee);
}
-template<class PolyKernel_, template<class Event> class EventComparison_>
+template<class FuncKernel_, template<class Event> class EventComparison_>
template<class Event_>
-typename Simulator<PolyKernel_, EventComparison_>::Key
-Simulator<PolyKernel_, EventComparison_>::
-add(const RationalFunction& f, const Event_& e)
+typename Simulator<FuncKernel_, EventComparison_>::Key
+Simulator<FuncKernel_, EventComparison_>::
+add(const Function& f, const Event_& e)
{
- Event* ee = new Event_(e);
- //std::cout << "Solving: " << f << std::endl;
- PolynomialKernel::solve(f, ee->root_stack());
- bool sign = PolynomialKernel::sign_at_negative_infinity(f);
- while (!ee->root_stack().empty() && ee->root_stack().top() < current_time())
- {
- ee->root_stack().pop();
- sign = !sign;
- }
- if (sign) ee->root_stack().pop(); // TODO: double-check the logic
- //std::cout << "Pushing: " << ee->root_stack().top() << std::endl;
- return queue_.push(ee);
-}
-
-template<class PolyKernel_, template<class Event> class EventComparison_>
-void
-Simulator<PolyKernel_, EventComparison_>::
-update(Key k, const RationalFunction& f)
-{
- Event* ee = *k;
- ee->root_stack() = RootStack(); // no clear() in std::stack
- PolynomialKernel::solve(f, ee->root_stack());
- while (!ee->root_stack().empty() && ee->root_stack().top() < current_time())
- ee->root_stack().pop();
- update(k);
+ Event* ee = new Event_(e);
+ rLog(rlSimulator, "Solving: %s", tostring(f).c_str());
+ int sign = FunctionKernel::sign_at_negative_infinity(f); // going to be sign after current time
+ rLog(rlSimulator, "Sign at -infinity: %i", sign);
+ if (sign != 0)
+ {
+ FunctionKernel::solve(f, ee->root_stack());
+ rLog(rlSimulator, "Got solution with root stack size: %i", ee->root_stack().size());
+ }
+
+ while (!ee->root_stack().empty() && ee->root_stack().top() < current_time())
+ {
+ // rLog(rlSimulator, "Popping expired root: %f", ee->root_stack().top());
+ ee->root_stack().pop();
+ sign *= -1;
+ }
+
+ if (sign == -1)
+ {
+ rLog(rlSimulator, "Popping the root because of negative sign (degeneracy)");
+ // rLog(rlSimulator, "Popping the root because of negative sign (degeneracy): %f", ee->root_stack().top());
+ // rLog(rlSimulator, " Current time: %f", current_time());
+ // AssertMsg(ee->root_stack().top() == current_time(),
+ // "If sign is negative, we must be in the degenerate case");
+ ee->root_stack().pop();
+ }
+
+ if (ee->root_stack().empty())
+ rLog(rlSimulator, "Pushing event with empty root stack");
+ else
+ {
+ rLog(rlSimulator, "Root stack size: %i", ee->root_stack().size());
+ rLog(rlSimulator, "Pushing: %s", tostring(ee->root_stack().top()).c_str());
+ }
+ Key k = queue_.push(ee);
+ return k;
}
-template<class PolyKernel_, template<class Event> class EventComparison_>
+//template<class FuncKernel_, template<class Event> class EventComparison_>
+//void
+//Simulator<FuncKernel_, EventComparison_>::
+//update(Key k, const Function& f)
+//{
+// Event* ee = *k;
+// Event* e = new Event;
+// e->root_stack() = RootStack(); // no clear() in std::stack
+// FunctionKernel::solve(f, e->root_stack());
+// while (!e->root_stack().empty() && e->root_stack().top() < current_time())
+// e->root_stack().pop();
+// queue_.replace(k, e);
+// delete ee;
+//}
+
+template<class FuncKernel_, template<class Event> class EventComparison_>
void
-Simulator<PolyKernel_, EventComparison_>::
+Simulator<FuncKernel_, EventComparison_>::
process()
{
- std::cout << "Queue size: " << queue_.size() << std::endl;
- Key top = queue_.top();
- Event* e = *top;
-
- if (e->root_stack().empty()) { reached_infinity_ = true; return; }
- else { current_ = e->root_stack().top(); e->root_stack().pop(); }
-
- if (e->process(this)) update(top);
- else { queue_.pop(); delete e; }
+ Count(cSimulatorProcess);
+ if (reached_infinity()) return;
+ rLog(rlSimulator, "Queue size: %i", queue_.size());
+ Key top = queue_.top();
+ Event* e = *top;
+ rLog(rlSimulator, "Processing event: %s", intostring(*e).c_str());
+
+ current_ = e->root_stack().top(); e->root_stack().pop();
+ queue_.demoted(top);
+
+ // Get the top element out of the queue, put it back depending on what process() says
+ if (!(e->process(this))) { queue_.remove(top); delete e; }
+
+ ++count_;
}
-template<class PolyKernel_, template<class Event> class EventComparison_>
-void
-Simulator<PolyKernel_, EventComparison_>::
-update(Key i)
-{
- queue_.update(i);
-}
-
-template<class PolyKernel_, template<class Event> class EventComparison_>
-typename Simulator<PolyKernel_, EventComparison_>::Time
-Simulator<PolyKernel_, EventComparison_>::
+template<class FuncKernel_, template<class Event> class EventComparison_>
+typename Simulator<FuncKernel_, EventComparison_>::Time
+Simulator<FuncKernel_, EventComparison_>::
audit_time() const
{
- const_Key top = queue_.top();
- Event* e = *top;
+ const_Key top = queue_.top();
+ Event* e = *top;
+
+ if (e->root_stack().empty()) return current_ + 1;
+ else return FunctionKernel::between(e->root_stack().top(), current_);
+}
- if (e->root_stack().empty()) return current_ + 1;
- else return PolynomialKernel::between(e->root_stack().top(), current_);
+template<class FuncKernel_, template<class Event> class EventComparison_>
+std::ostream&
+Simulator<FuncKernel_, EventComparison_>::
+operator<<(std::ostream& out) const
+{
+ out << "Simulator: " << std::endl;
+ return queue_.print(out, " ");
}
-
-template<class PolyKernel_, template<class Event> class EventComparison_>
+
+template<class FuncKernel_, template<class Event> class EventComparison_>
std::ostream&
-Simulator<PolyKernel_, EventComparison_>::
-print(std::ostream& out) const
+operator<<(std::ostream& out, const Simulator<FuncKernel_, EventComparison_>& s)
{
- out << "Simulator: " << std::endl;
- return queue_.print(out, " ");
+ return s.operator<<(out);
}
+
+template<class FuncKernel_, template<class Event> class EventComparison_>
+std::ostream&
+operator<<(std::ostream& out, const typename Simulator<FuncKernel_, EventComparison_>::Event& e)
+{
+ return e.operator<<(out);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/geometry/weighted-cechdistance.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,55 @@
+#ifndef __L2_DISTANCE_H__
+#define __L2_DISTANCE_H__
+
+#include <utilities/types.h>
+
+#include <vector>
+#include <fstream>
+#include <functional>
+#include <cmath>
+#include <string>
+#include <sstream>
+
+
+typedef std::vector<double> Point;
+typedef std::vector<Point> PointContainer;
+
+struct WeightedL2Distance:
+ public std::binary_function<const Point&, const Point&, double>
+{
+ result_type operator()(const Point& p1, const Point& p2) const
+ {
+ AssertMsg(p1.size() == p2.size(), "Points must be in the same dimension (in L2Distance): dim1=%d, dim2=%d", p1.size()-1, p2.size()-1);
+
+ /* the distance of a point to itself is the radius at which the "power distance" ball around it appears:
+ d(p,p) := sqrt(-w_p) */
+ if (p1 == p2)
+ return sqrt(-p1[p1.size()-1]);
+
+ /* otherwise, the distance is the radius at which the power balls around p, q intersect:
+ d(p,q) := sqrt( ( (w_p-w_q)^2 / 4||p-q||^2 ) - (w_p+w_q) / 2 + ||p-q||^2 / 4 ) */
+ result_type sq_l2dist = 0;
+ result_type wp = p1[p1.size()-1];
+ result_type wq = p2[p2.size()-1];
+ for (size_t i = 0; i < p1.size()-1; ++i)
+ sq_l2dist += (p1[i] - p2[i])*(p1[i] - p2[i]);
+ return sqrt( (wp-wq)*(wp-wq) / (4 * sq_l2dist) - (wp+wq) / 2 + sq_l2dist / 4 );
+ }
+};
+
+void read_weighted_points(const std::string& infilename, PointContainer& points)
+{
+ std::ifstream in(infilename.c_str());
+ std::string line;
+ while(std::getline(in, line))
+ {
+ if (line[0] == '#') continue; // comment line in the file
+ std::stringstream linestream(line);
+ double x;
+ points.push_back(Point());
+ while (linestream >> x)
+ points.back().push_back(x);
+ }
+}
+
+#endif // __L2_DISTANCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/geometry/weighted-l2distance.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,54 @@
+#ifndef __L2_DISTANCE_H__
+#define __L2_DISTANCE_H__
+
+#include <utilities/types.h>
+#include <utilities/log.h>
+
+#include <vector>
+#include <fstream>
+#include <functional>
+#include <cmath>
+#include <string>
+#include <sstream>
+
+
+typedef std::vector<double> Point;
+typedef std::vector<Point> PointContainer;
+
+struct WeightedL2Distance:
+ public std::binary_function<const Point&, const Point&, double>
+{
+ result_type operator()(const Point& p1, const Point& p2) const
+ {
+ AssertMsg(p1.size() == p2.size(), "Points must be in the same dimension (in L2Distance): dim1=%d, dim2=%d", p1.size()-1, p2.size()-1);
+
+ /* the distance of a point to itself is the radius at which the "power distance" ball around it appears:
+ d(p,p) := sqrt(-w_p) */
+ if (p1 == p2)
+ return sqrt(-p1[p1.size()-1]);
+
+ /* otherwise, the distance is the radius at which the power balls around p, q intersect:
+ d(p,q) := sqrt( ||p-q||^2 - w_p - w_q ) */
+ result_type sq_l2dist = 0;
+ for (size_t i = 0; i < p1.size()-1; ++i)
+ sq_l2dist += (p1[i] - p2[i])*(p1[i] - p2[i]);
+ return sqrt(sq_l2dist - p1[p1.size()-1] - p2[p2.size()-1]);
+ }
+};
+
+void read_weighted_points(const std::string& infilename, PointContainer& points)
+{
+ std::ifstream in(infilename.c_str());
+ std::string line;
+ while(std::getline(in, line))
+ {
+ if (line[0] == '#') continue; // comment line in the file
+ std::stringstream linestream(line);
+ double x;
+ points.push_back(Point());
+ while (linestream >> x)
+ points.back().push_back(x);
+ }
+}
+
+#endif // __L2_DISTANCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/chain.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,129 @@
+/**
+ * Author: Dmitriy Morozov
+ * Department of Computer Science, Duke University, 2005-2008
+ */
+
+#ifndef __CHAIN_H__
+#define __CHAIN_H__
+
+//#include "utilities/types.h"
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/serialization.hpp>
+#include <boost/iterator/iterator_traits.hpp>
+#include <boost/optional.hpp>
+#include <iterator>
+#include <string>
+
+#include <utilities/containers.h>
+
+/**
+ * Class: ChainWrapper
+ * Class storing a chain of simplices. Stores items in the order defined by ConsistencyCmp.
+ * The actual order of the elements is defined by OrderCmp. Instances of those
+ * classes are not stored in Chain for efficiency, and are passed as arguments to those methods
+ * that require them.
+ *
+ * \ingroup topology
+ *
+ * TODO: add field (defaulting to Z_2)
+ */
+template <class Container_>
+class ChainWrapper: public Container_,
+ public SizeStorage<Container_>
+{
+ public:
+ /// \name Template Parameters
+ /// @{
+ typedef Container_ Container;
+ typedef SizeStorage<Container> Size;
+ typedef typename boost::iterator_value<typename Container::iterator>::type Item;
+ /// @}
+
+ typedef ChainWrapper Self;
+ typedef Container ChainRepresentation;
+
+ /// \name Accessor typedefs
+ /// @{
+ typedef typename ChainRepresentation::iterator iterator;
+ typedef typename ChainRepresentation::const_iterator const_iterator;
+ typedef typename ChainRepresentation::const_reference const_reference;
+ typedef typename ChainRepresentation::reference reference;
+ typedef typename ChainRepresentation::pointer pointer;
+ typedef typename ChainRepresentation::const_pointer const_pointer;
+ typedef Item value_type;
+ /// @}
+
+ public:
+ ChainWrapper();
+ ChainWrapper(const ChainWrapper& c);
+ template<class Iterator> ChainWrapper(Iterator bg, Iterator end);
+
+ /// \name Whole Chain operations
+ /// @{
+ /** Add c to *this assuming both Chains are sorted in increasing order according to cmp. */
+ template<class ConsistencyComparison>
+ Self& add(const Self& c, const ConsistencyComparison& cmp);
+
+ void swap(ChainWrapper& c); ///< Swaps the contents of c and *this (like STL's swap destroys c)
+ void clear();
+
+ template<class ConsistencyComparison>
+ void sort(const ConsistencyComparison& cmp); ///< Sort elements to enforce consistency
+
+ size_t size() const { return Size::size(*this); }
+
+ using ChainRepresentation::empty;
+ /// @}
+
+ /// \name Modifiers
+ /// @{
+ void remove(iterator i) { ChainRepresentation::erase(i); Size::operator--(); }
+ void remove(const_reference x) { remove(std::find(begin(), end(), x)); }
+ bool remove_if_contains(const_reference x);
+
+ template<class ConsistencyComparison>
+ void append(const_reference x, const ConsistencyComparison& cmp);
+ /// @}
+
+ /// \name Accessors
+ /// @{
+ using ChainRepresentation::begin;
+ using ChainRepresentation::end;
+ using ChainRepresentation::front;
+ using ChainRepresentation::back;
+
+ template<class OrderComparison>
+ const_reference top(const OrderComparison& cmp) const; ///< First element in cmp order
+
+ boost::optional<const_iterator> contains(const_reference x) const; ///< tests whether chain contains x
+ boost::optional<iterator> contains(const_reference x); ///< tests whether chain contains x
+ /// @}
+
+ /// \name Debugging
+ /// @{
+ template<class OrderComparison>
+ const_reference get_first(const OrderComparison& cmp) const; ///< First element in cmp order
+
+ template<class OutputMap>
+ std::string tostring(const OutputMap& outmap = OutputMap()) const;
+ /// @}
+
+ /// Technically, it's an internal operation, and should be followed by a sort()
+ using ChainRepresentation::push_back;
+
+ private:
+ using ChainRepresentation::insert;
+ using ChainRepresentation::erase;
+
+ private:
+ // Serialization
+ typedef Container Parent;
+ friend class boost::serialization::access;
+ template<class Archive>
+ void serialize(Archive& ar, boost::serialization::version_type );
+};
+
+#include "chain.hpp"
+
+#endif // __CHAIN_H__
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/chain.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,182 @@
+#include <algorithm>
+#include <vector>
+#include "utilities/containers.h"
+
+#include <boost/serialization/split_member.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/binary_object.hpp>
+#include <boost/utility.hpp>
+
+#include "utilities/log.h"
+#include "utilities/counter.h"
+
+using boost::serialization::make_nvp;
+using boost::serialization::make_binary_object;
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlChain = DEF_CHANNEL( "topology/chain", rlog::Log_Debug);
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cChainAddBasic = GetCounter("chain/add/basic");
+static Counter* cChainAddComparison = GetCounter("chain/add/comparison");
+#endif // COUNTERS
+
+template<class C>
+ChainWrapper<C>::
+ChainWrapper()
+{}
+
+template<class C>
+ChainWrapper<C>::
+ChainWrapper(const ChainWrapper& c):
+ ChainRepresentation(c), Size(c)
+{}
+
+template<class C>
+template<class Iterator>
+ChainWrapper<C>::
+ChainWrapper(Iterator bg, Iterator end):
+ ChainRepresentation(bg, end), Size(ChainRepresentation::size())
+{}
+
+template<class C>
+template<class ConsistencyCmp>
+void
+ChainWrapper<C>::
+append(const_reference x, const ConsistencyCmp& cmp)
+{
+ Size::operator++();
+
+ // First try the special cases that x goes at the end
+ if (empty() || cmp(back(), x))
+ push_back(x);
+ // Then try the special case that x goes at the front
+ else if (cmp(x, front()))
+ ContainerTraits<C,ConsistencyCmp>::push_front(*this, x);
+ else
+ insert(std::upper_bound(begin(), end(), x, cmp), x);
+}
+
+template<class C>
+template<class OrderComparison>
+typename ChainWrapper<C>::const_reference
+ChainWrapper<C>::
+top(const OrderComparison& cmp) const
+{
+ AssertMsg(!empty(), "Chain must not be empty for low()");
+ return *std::min_element(begin(), end(), cmp);
+}
+
+template<class C>
+void
+ChainWrapper<C>::
+swap(ChainWrapper& c)
+{
+ ChainRepresentation::swap(c);
+ Size::swap(c);
+}
+
+template<class C>
+void
+ChainWrapper<C>::
+clear()
+{
+ ChainRepresentation::clear();
+ Size::clear();
+}
+
+template<class C>
+template<class ConsistencyComparison>
+void
+ChainWrapper<C>::
+sort(const ConsistencyComparison& cmp)
+{
+ ContainerTraits<C,ConsistencyComparison>::sort(*this, cmp);
+}
+
+template<class C>
+boost::optional<typename ChainWrapper<C>::const_iterator>
+ChainWrapper<C>::
+contains(const_reference x) const
+{
+ const_iterator res = std::find(begin(), end(), x);
+ return boost::make_optional(res != end(), res);
+}
+
+template<class C>
+boost::optional<typename ChainWrapper<C>::iterator>
+ChainWrapper<C>::
+contains(const_reference x)
+{
+ iterator res = std::find(begin(), end(), x);
+ return boost::make_optional(res != end(), res);
+}
+
+template<class C>
+bool
+ChainWrapper<C>::
+remove_if_contains(const_reference x)
+{
+ boost::optional<iterator> i = contains(x);
+ if (i)
+ {
+ remove(*i);
+ return true;
+ } else
+ return false;
+}
+
+template<class C>
+template<class OutputMap>
+std::string
+ChainWrapper<C>::
+tostring(const OutputMap& outmap) const
+{
+ std::string str;
+ for (const_iterator cur = begin(); cur != end(); ++cur)
+ {
+ if (cur != begin()) str += ", ";
+ str += outmap(*cur);
+ }
+ return str;
+}
+
+template<class C>
+template<class ConsistencyCmp>
+typename ChainWrapper<C>::Self&
+ChainWrapper<C>::
+add(const Self& c, const ConsistencyCmp& cmp)
+{
+ // TODO: tmp-based addition is necessary and useful for Containers that are vectors,
+ // however, I believe it creates costly overhead for Containers that are lists.
+ // Need to put some thought into this.
+ ChainRepresentation tmp;
+
+ CountingBackInserter<ChainRepresentation> bi(tmp);
+ std::set_symmetric_difference(begin(), end(), c.begin(), c.end(), bi, cmp);
+
+ CountBy(cChainAddBasic, size() + c.size() - (size() + c.size() - tmp.size())/2);
+
+ static_cast<ChainRepresentation*>(this)->swap(tmp);
+ static_cast<Size*>(this)->swap(bi);
+
+ return *this;
+}
+
+template<class C>
+template<class OrderComparison>
+typename ChainWrapper<C>::const_reference
+ChainWrapper<C>::
+get_first(const OrderComparison& cmp) const
+{ return top(cmp); }
+
+
+template<class C>
+template<class Archive>
+void
+ChainWrapper<C>::
+serialize(Archive& ar, boost::serialization::version_type )
+{
+ ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Parent);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/cohomology-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,138 @@
+#ifndef __COHOMOLOGY_PERSISTENCE_H__
+#define __COHOMOLOGY_PERSISTENCE_H__
+
+#if DEBUG_CONTAINERS
+ #include <debug/list>
+ #include <debug/vector>
+ namespace s = std::__debug;
+ #warning "Using debug/list and debug/vector in CohomologyPersistence"
+#else
+ #include <list>
+ #include <vector>
+ namespace s = std;
+#endif
+
+#include <vector>
+#include <list>
+#include <utility>
+
+#include <topology/field-arithmetic.h>
+#include "utilities/types.h"
+
+#include <boost/optional.hpp>
+#include <boost/intrusive/list.hpp>
+namespace bi = boost::intrusive;
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/shared_ptr.hpp>
+
+
+template<class BirthInfo_, class SimplexData_ = Empty<>, class Field_ = ZpField>
+class CohomologyPersistence
+{
+ public:
+ typedef BirthInfo_ BirthInfo;
+ typedef SimplexData_ SimplexData;
+ typedef Field_ Field;
+
+ typedef typename Field::Element FieldElement;
+
+
+ CohomologyPersistence(const Field& field = Field()):
+ field_(field), image_begin_(cocycles_.end()) {}
+
+
+ // An entry in a cocycle column
+ struct SNode; // members: si, coefficient, ci
+ typedef s::vector<SNode> ZColumn;
+ typedef bi::list<SNode, bi::constant_time_size<false> > ZRow;
+ class CompareSNode;
+
+ struct SHead; // members: row, order
+ typedef s::list<SHead> Simplices;
+ typedef typename Simplices::iterator SimplexIndex;
+
+ struct Cocycle; // members: zcolumn, birth, order
+ typedef s::list<Cocycle> Cocycles;
+ typedef typename Cocycles::iterator CocycleIndex;
+ typedef std::pair<CocycleIndex, FieldElement> CocycleCoefficientPair;
+
+ typedef boost::optional<BirthInfo> Death;
+ typedef boost::shared_ptr<ZColumn> CocyclePtr;
+ typedef boost::tuple<SimplexIndex, Death, CocyclePtr> IndexDeathCocycle;
+
+ // return either a SimplexIndex or a Death
+ // BI = BoundaryIterator; it should dereference to a SimplexIndex
+ template<class BI>
+ IndexDeathCocycle add(BI begin, BI end, BirthInfo b, bool store = true, const SimplexData& sd = SimplexData(), bool image = true);
+
+ // if sign needs to be specified explicitly, provide (parallel) coefficient_iter
+ template<class BI, class CI>
+ IndexDeathCocycle add(CI coefficient_iter, BI begin, BI end, BirthInfo b, bool store = true, const SimplexData& sd = SimplexData(), bool image = true);
+
+ void show_cocycles() const;
+ CocycleIndex begin() { return image_begin_; }
+ CocycleIndex end() { return cocycles_.end(); }
+
+ private:
+ void add_cocycle(CocycleCoefficientPair& z1, CocycleCoefficientPair& z2);
+
+ private:
+ Simplices simplices_;
+ Cocycles cocycles_;
+ CocycleIndex image_begin_;
+ Field field_;
+};
+
+// Simplex representation
+template<class BirthInfo_, class SimplexData_, class Field_>
+struct CohomologyPersistence<BirthInfo_, SimplexData_, Field_>::SHead: public SimplexData
+{
+ SHead(const SHead& other):
+ SimplexData(other), order(other.order) {} // don't copy row since we can't
+ SHead(const SimplexData& sd, unsigned o):
+ SimplexData(sd), order(o) {}
+
+ // intrusive list corresponding to row of s in Z^*, not ordered in any particular order
+ ZRow row;
+ unsigned order;
+};
+
+// An entry in a cocycle column; it's also an element in an intrusive list, hence the list_base_hook<>
+typedef bi::list_base_hook<bi::link_mode<bi::auto_unlink> > auto_unlink_hook;
+template<class BirthInfo_, class SimplexData_, class Field_>
+struct CohomologyPersistence<BirthInfo_, SimplexData_, Field_>::SNode: public auto_unlink_hook
+{
+ SNode(const SNode& other):
+ si(other.si), coefficient(other.coefficient),
+ ci(other.ci) {}
+
+ SNode(SimplexIndex sidx, FieldElement coef, CocycleIndex cidx):
+ si(sidx), coefficient(coef), ci(cidx) {}
+
+ SimplexIndex si;
+ FieldElement coefficient;
+
+ CocycleIndex ci; // TODO: is there no way to get rid of this overhead?
+
+ void unlink() { auto_unlink_hook::unlink(); }
+};
+
+template<class BirthInfo_, class SimplexData_, class Field_>
+struct CohomologyPersistence<BirthInfo_, SimplexData_, Field_>::Cocycle
+{
+ Cocycle(const BirthInfo& b, unsigned o):
+ birth(b), order(o) {}
+
+ ZColumn zcolumn;
+ BirthInfo birth;
+ signed order;
+
+ bool operator<(const Cocycle& other) const { return order > other.order; }
+ bool operator==(const Cocycle& other) const { return order == other.order; }
+};
+
+
+#include "cohomology-persistence.hpp"
+
+#endif // __COHOMOLOGY_PERSISTENCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/cohomology-persistence.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,292 @@
+#include <boost/utility.hpp>
+#include <queue>
+#include <vector>
+#include <limits>
+
+#include <utilities/log.h>
+#include <utilities/indirect.h>
+#include <utilities/counter.h>
+
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/iterator/counting_iterator.hpp>
+#include <boost/foreach.hpp>
+
+#include <boost/lambda/lambda.hpp>
+#include <boost/lambda/bind.hpp>
+namespace bl = boost::lambda;
+
+#include <boost/make_shared.hpp>
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlCohomology = DEF_CHANNEL("topology/cohomology", rlog::Log_Debug);
+#endif
+
+#ifdef COUNTERS
+static Counter* cCohomologyAddBasic = GetCounter("cohomology/add/basic");
+static Counter* cCohomologyAddComparison = GetCounter("cohomology/add/comparison");
+static Counter* cCohomologyElementCount = GetCounter("cohomology/elements");
+static Counter* cCohomologyCocycleCount = GetCounter("cohomology/cocycles");
+static Counter* cCohomologyCandidatesCount = GetCounter("cohomology/candidates");
+#endif // COUNTERS
+
+template<class BirthInfo, class SimplexData, class Field>
+class CohomologyPersistence<BirthInfo, SimplexData, Field>::CompareSNode
+{
+ public:
+ bool operator()(const SNode& s1, const SNode& s2) const { return s1.si->order < s2.si->order; }
+};
+
+
+struct Alternator
+{
+ typedef int result_type;
+ int operator()(int x) const { return (x % 2)*2 - 1; }
+};
+
+
+template<class BirthInfo, class SimplexData, class Field>
+template<class BI>
+typename CohomologyPersistence<BirthInfo, SimplexData, Field>::IndexDeathCocycle
+CohomologyPersistence<BirthInfo, SimplexData, Field>::
+add(BI begin, BI end, BirthInfo birth, bool store, const SimplexData& sd, bool image)
+{
+ // Set coefficient to be an iterator over (-1)^i
+
+ return add(boost::make_transform_iterator(boost::make_counting_iterator(1), Alternator()),
+ begin, end,
+ birth, store, sd, image);
+}
+
+
+template<class BirthInfo, class SimplexData, class Field>
+template<class BI, class CI>
+typename CohomologyPersistence<BirthInfo, SimplexData, Field>::IndexDeathCocycle
+CohomologyPersistence<BirthInfo, SimplexData, Field>::
+add(CI coefficient_iter, BI begin, BI end, BirthInfo birth, bool store, const SimplexData& sd, bool image)
+{
+ // Create simplex representation
+ simplices_.push_back(SHead(sd, simplices_.empty() ? 0 : (simplices_.back().order + 1)));
+ SimplexIndex si = boost::prior(simplices_.end());
+
+ // Find out if there are cocycles that evaluate to non-zero on the new simplex
+ typedef std::list<CocycleCoefficientPair> Candidates;
+ Candidates candidates, candidates_bulk;
+ rLog(rlCohomology, "Boundary");
+
+ for (BI cur = begin; cur != end; ++cur)
+ {
+ FieldElement coefficient = field_.init(*coefficient_iter++);
+ SimplexIndex cursi = *cur;
+
+ rLog(rlCohomology, " %d %d", cursi->order, coefficient);
+ BOOST_FOREACH(const SNode& zcur, std::make_pair(cursi->row.begin(), cursi->row.end()))
+ candidates_bulk.push_back(std::make_pair(zcur.ci, field_.mul(coefficient, zcur.coefficient)));
+ }
+
+ candidates_bulk.sort(make_first_comparison(make_indirect_comparison(std::less<Cocycle>())));
+ CountBy(cCohomologyCandidatesCount, candidates_bulk.size());
+
+#if LOGGING
+ rLog(rlCohomology, " Candidates bulk");
+ for (typename Candidates::iterator cur = candidates_bulk.begin();
+ cur != candidates_bulk.end(); ++cur)
+ rLog(rlCohomology, " %d %d", cur->first->order, cur->second);
+#endif
+
+ // Remove duplicates
+ {
+ typename Candidates::const_iterator cur = candidates_bulk.begin();
+ while (cur != candidates_bulk.end())
+ {
+ typename Candidates::const_iterator next = cur;
+ FieldElement sum = field_.zero();
+ while (next != candidates_bulk.end() && next->first == cur->first)
+ {
+ sum = field_.add(sum, next->second);
+ ++next;
+ }
+
+ rLog(rlCohomology, " Bulk candidate %d sum %d", cur->first->order, sum);
+ if (!field_.is_zero(sum))
+ candidates.push_back(CocycleCoefficientPair(cur->first, sum));
+
+ cur = next;
+ }
+ }
+
+ // Birth
+ if (candidates.empty())
+ {
+ // rLog(rlCohomology, " Birth occurred");
+ if (!store)
+ {
+ simplices_.pop_back();
+ boost::shared_ptr<ZColumn> p = boost::make_shared<ZColumn>();
+ return boost::make_tuple(simplices_.end(), Death(), p);
+ }
+
+ signed order = 0;
+ if (image)
+ if (image_begin_ == cocycles_.end())
+ order = std::numeric_limits<signed>::min();
+ else
+ order = image_begin_->order + 1;
+ else
+ if (!cocycles_.empty() && cocycles_.front().order >= 0) // we have something outside the image
+ order = cocycles_.front().order + 1;
+
+ CocycleIndex nw;
+ if (image)
+ {
+ image_begin_ = cocycles_.insert(image_begin_, Cocycle(birth, order));
+ nw = image_begin_;
+ } else
+ {
+ cocycles_.push_front(Cocycle(birth, order));
+ nw = cocycles_.begin();
+ }
+
+ rLog(rlCohomology, "Birth: %d", nw->order);
+
+ // set up the cocycle
+ ZColumn& cocycle = nw->zcolumn;
+ cocycle.push_back(SNode(si, field_.id(), nw));
+ si->row.push_back(cocycle.front());
+ rLog(rlCohomology, " Cocyle: %d", si->order);
+
+ Count(cCohomologyElementCount);
+ Count(cCohomologyCocycleCount);
+
+ boost::shared_ptr<ZColumn> p = boost::make_shared<ZColumn>();
+ return boost::make_tuple(si, Death(), p);
+ }
+
+ // Death
+ rLog(rlCohomology, "Death");
+
+#if LOGGING
+ // Debug only, output candidates
+ rLog(rlCohomology, " Candidates");
+ for (typename Candidates::iterator cur = candidates.begin(); cur != candidates.end(); ++cur)
+ rLog(rlCohomology, " %d %d", cur->first->order, cur->second);
+#endif
+
+ CocycleCoefficientPair& z = candidates.front();
+ Death d = z.first->birth;
+ rLog(rlCohomology, " Order: %d", z.first->order);
+ if (z.first->order >= 0) // if death outside image
+ d = Death(); // no death occurs outside the image
+ else
+ if (z.first == image_begin_)
+ ++image_begin_;
+
+ // add z to everything else in candidates
+ for (typename Candidates::iterator cur = boost::next(candidates.begin());
+ cur != candidates.end(); ++cur)
+ {
+ CountBy(cCohomologyElementCount, -cur->first->zcolumn.size());
+ add_cocycle(*cur, z);
+ CountBy(cCohomologyElementCount, cur->first->zcolumn.size());
+ }
+
+ for (typename ZColumn::iterator cur = z.first->zcolumn.begin(); cur != z.first->zcolumn.end(); ++cur)
+ cur->unlink();
+
+ CountBy(cCohomologyElementCount, -z.first->zcolumn.size());
+ CountBy(cCohomologyCocycleCount, -1);
+ boost::shared_ptr<ZColumn> p = boost::make_shared<ZColumn>();
+ p->swap(z.first->zcolumn);
+ cocycles_.erase(z.first);
+
+ return boost::make_tuple(si, d, p);
+}
+
+template<class BirthInfo, class SimplexData, class Field>
+void
+CohomologyPersistence<BirthInfo, SimplexData, Field>::
+show_cocycles() const
+{
+ std::cout << "Cocycles: " << cocycles_.size() << std::endl;
+ for (typename Cocycles::const_iterator cur = cocycles_.begin(); cur != cocycles_.end(); ++cur)
+ {
+ // std::cout << cur->order << " (" << cur->birth << "): ";
+ for (typename ZColumn::const_iterator zcur = cur->zcolumn.begin(); zcur != cur->zcolumn.end(); ++zcur)
+ std::cout << zcur->coefficient << " * " << zcur->si->order << ", ";
+ std::cout << std::endl;
+ }
+}
+
+template<class BirthInfo, class SimplexData, class Field>
+void
+CohomologyPersistence<BirthInfo, SimplexData, Field>::
+add_cocycle(CocycleCoefficientPair& to, CocycleCoefficientPair& from)
+{
+ rLog(rlCohomology, "Adding cocycle %d to %d", from.first->order, to.first->order);
+
+ FieldElement multiplier = field_.neg(field_.div(to.second, from.second));
+ CocycleIndex ci = to.first;
+ CompareSNode cmp;
+
+ // Insert at the end optimization
+ if (cmp(to.first->zcolumn.back(), from.first->zcolumn.front()) &&
+ to.first->zcolumn.capacity() >= (to.first->zcolumn.size() + from.first->zcolumn.size()))
+ {
+ BOOST_FOREACH(const SNode& fs, from.first->zcolumn)
+ {
+ to.first->zcolumn.push_back(SNode(fs.si, field_.mul(multiplier, fs.coefficient), ci));
+ fs.si->row.push_back(to.first->zcolumn.back());
+ Count(cCohomologyAddBasic);
+ }
+
+ return;
+ }
+
+ ZColumn nw;
+ typename ZColumn::iterator tcur = to.first->zcolumn.begin();
+ typename ZColumn::iterator fcur = from.first->zcolumn.begin();
+ while (tcur != to.first->zcolumn.end() && fcur != from.first->zcolumn.end())
+ {
+ rLog(rlCohomology, " %d %d", tcur->si->order, fcur->si->order);
+ Count(cCohomologyAddComparison);
+ Count(cCohomologyAddBasic);
+ if (cmp(*tcur, *fcur))
+ {
+ nw.push_back(*tcur);
+ ++tcur;
+ }
+ else if (cmp(*fcur, *tcur))
+ {
+ nw.push_back(SNode(fcur->si, field_.mul(multiplier, fcur->coefficient), ci));
+ ++fcur;
+ }
+ else // equality
+ {
+ FieldElement res = field_.mul(multiplier, tcur->coefficient);
+ res = field_.add(fcur->coefficient, res);
+ if (!field_.is_zero(res))
+ nw.push_back(SNode(fcur->si, res, ci));
+ ++tcur; ++fcur;
+ }
+ }
+ for (; tcur != to.first->zcolumn.end(); ++tcur)
+ {
+ rLog(rlCohomology, " %d", tcur->si->order);
+ Count(cCohomologyAddBasic);
+ nw.push_back(SNode(*tcur));
+ }
+ for (; fcur != from.first->zcolumn.end(); ++fcur)
+ {
+ rLog(rlCohomology, " %d", fcur->si->order);
+ Count(cCohomologyAddBasic);
+ nw.push_back(SNode(fcur->si, field_.mul(multiplier, fcur->coefficient), ci));
+ }
+
+
+ for (typename ZColumn::iterator cur = to.first->zcolumn.begin(); cur != to.first->zcolumn.end(); ++cur)
+ cur->unlink();
+
+ to.first->zcolumn.swap(nw);
+
+ for (typename ZColumn::iterator cur = to.first->zcolumn.begin(); cur != to.first->zcolumn.end(); ++cur)
+ cur->si->row.push_back(*cur);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/complex-traits.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,77 @@
+#ifndef __COMPLEX_TRAITS_H__
+#define __COMPLEX_TRAITS_H__
+
+#include <vector>
+#include <map>
+#include "utilities/property-maps.h"
+
+// Class: ComplexTraits
+// Class that extracts various properties of a complex.
+//
+// Parameter:
+// Complex - the type whose traits we are extracting
+template<class Complex_>
+struct ComplexTraits
+{
+ /*
+ * Typedefs: Associated types
+ * Types extractable by the traits class.
+ *
+ * Complex - the type of the complex
+ * Simplex - the type of the simplex stored in the complex
+ * Index - the type used to refer to the elements of the complex
+ * IndexComparison - the type used to compare indices
+ */
+ typedef Complex_ Complex;
+ typedef typename Complex::Simplex Simplex;
+ typedef typename Complex::Index Index;
+ typedef std::less<Index> IndexComparison;
+
+ /* Typedefs: Maps
+ * Maps to go from Indicies to Simplices and back.
+ *
+ * SimplexIndexMap - the map from Simplex to the Index (could be implemented via a binary search, or a map)
+ */
+ typedef AssociativeMap<std::map<Simplex, Index,
+ typename Simplex::VertexComparison> > SimplexIndexMap;
+
+ // Function: simplex_index_map()
+ // Initializes an SimplexIndexMap given a Complex.
+ static SimplexIndexMap
+ simplex_index_map(const Complex& complex) { return SimplexIndexMap(complex.begin(),
+ complex.end()); }
+
+ static Index begin(const Complex& complex) { return complex.begin(); }
+ static Index end(const Complex& complex) { return complex.end(); }
+
+ static const Simplex&
+ simplex(Index i) { return *i; }
+};
+
+
+// Specializations
+template<class Simplex_>
+struct ComplexTraits<std::vector<Simplex_> >
+{
+ typedef std::vector<Simplex_> Complex;
+ typedef Simplex_ Simplex;
+ typedef typename Complex::const_iterator Index;
+ typedef std::less<Index> IndexComparison;
+
+ typedef BinarySearchMap<Simplex, Index,
+ typename Simplex::VertexComparison> SimplexIndexMap;
+
+ static SimplexIndexMap
+ simplex_index_map(const Complex& complex) { return SimplexIndexMap(complex.begin(),
+ complex.end()); }
+ static SimplexIndexMap
+ simplex_index_map(Index bg, Index end) { return SimplexIndexMap(bg, end); }
+
+ static Index begin(const Complex& complex) { return complex.begin(); }
+ static Index end(const Complex& complex) { return complex.end(); }
+
+ static const Simplex&
+ simplex(Index i) { return *i; }
+};
+
+#endif // __COMPLEX_TRAITS_H__
--- a/include/topology/conesimplex.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/conesimplex.h Tue Jun 27 09:37:05 2017 -0700
@@ -9,30 +9,54 @@
#include <list>
#include <iostream>
-template<class S>
-class ConeSimplex: public S
+#include "utilities/types.h"
+#include "simplex.h"
+
+
+template<class V, class T = Empty>
+class ConeSimplex: public Simplex<V,T>
{
- public:
- typedef S Parent;
- typedef ConeSimplex<S> Self;
- typedef std::list<Self> Cycle;
+ public:
+ typedef Simplex<V,T> Parent;
+ typedef ConeSimplex<V,T> Self;
public:
- ConeSimplex(const Parent& parent,
- bool coned = false):
- Parent(parent), coned_(coned) {}
-
- Cycle boundary() const;
- bool coned() const { return coned_; }
+ ConeSimplex(const Self& s):
+ Parent(s), coned_(s.coned_) {}
+ ConeSimplex(const Parent& parent,
+ bool coned = false):
+ Parent(parent), coned_(coned) {}
+
+ Cycle boundary() const;
+ bool coned() const { return coned_; }
+ Dimension dimension() const { return coned_ ? (Parent::dimension() + 1) : Parent::dimension(); }
+
+ bool operator==(const Self& other) const { return !(coned_ ^ other.coned_) && Parent::operator==(other); }
- std::ostream& operator<<(std::ostream& out) const;
-
- private:
- bool coned_;
+ std::ostream& operator<<(std::ostream& out) const;
+
+ struct ConedVertexComparison;
+
+ private:
+ bool coned_;
};
-template<class S>
-std::ostream& operator<<(std::ostream& out, const ConeSimplex<S>& s) { return s.operator<<(out); }
+template<class V, class T>
+struct ConeSimplex<V,T>::ConedVertexComparison: public typename Simplex<V,T>::VertexComparison
+{
+ typedef typename Simplex<V,T>::VertexComparison Parent;
+
+ bool operator()(const Self& a, const Self& b) const
+ {
+ if (a.coned() ^ b.coned())
+ return b.coned(); // coned simplices shall come after non-coned ones
+ else
+ return Parent::operator()(a,b); // within coned/non-coned order by vertices
+ }
+};
+
+template<class V, class T>
+std::ostream& operator<<(std::ostream& out, const ConeSimplex<V,T>& s) { return s.operator<<(out); }
#include "conesimplex.hpp"
--- a/include/topology/cycle.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/**
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005-2006
- */
-
-#ifndef __CYCLE_H__
-#define __CYCLE_H__
-
-#include "utilities/sys.h"
-#include "utilities/debug.h"
-
-#include "utilities/types.h"
-#include "utilities/circular_list.h"
-#include <list>
-#include <boost/serialization/access.hpp>
-
-/**
- * Class storing a cycle of simplices. Stores items in the order defined by ConsistencyCmp.
- * The actual order of the elements is defined by OrderCmp. Instances of those
- * classes are not stored in Cycle for efficiency, and are passed as arguments to those methods
- * that require them.
- */
-template <class Itm, class OrderCmp, class ConsistencyCmp = OrderCmp>
-class Cycle: public List<Itm>
-{
- public:
- /// \name Template Parameters
- /// @{
- typedef Itm Item;
- typedef OrderCmp OrderComparison;
- typedef ConsistencyCmp ConsistencyComparison;
- /// @}
-
- typedef Cycle<Item, OrderComparison, ConsistencyCmp> Self;
- typedef List<Item> CycleRepresentation;
-
- /// \name Accessor typedefs
- /// @{
- typedef typename CycleRepresentation::iterator iterator;
- typedef typename CycleRepresentation::const_iterator const_iterator;
- typedef typename CycleRepresentation::const_reference const_reference;
- typedef typename CycleRepresentation::reference reference;
- typedef typename CycleRepresentation::pointer pointer;
- typedef typename CycleRepresentation::const_pointer const_pointer;
- /// @}
-
- public:
- Cycle();
- Cycle(const Cycle& c);
-
- /// \name Whole Cycle operations
- /// @{
- /** Add c to *this assuming both Cycles are sorted in increasing order according to cmp. */
- Self& add(const Self& c, const ConsistencyComparison& cmp);
- void swap(Cycle& c); ///< Swaps the contents of c and *this (like STL's swap destroys c)
- //void insertion_sort(const Comparison& cmp); ///< Sort list[i] using insertion sort
- void sort(const ConsistencyComparison& cmp); ///< Sort elements to enforce consistency
- using CycleRepresentation::empty;
- using CycleRepresentation::clear;
- using CycleRepresentation::size;
- /// @}
-
- /// \name Modifiers
- /// @{
- using CycleRepresentation::erase;
- void append(const_reference x, const ConsistencyComparison& cmp);
- /// @}
-
- /// \name Accessors
- /// @{
- using CycleRepresentation::begin;
- using CycleRepresentation::end;
- const_reference top(const OrderComparison& cmp) const; ///< First element in cmp order
- iterator get_second(const OrderComparison& cmp) const; ///< Second element in cmp order
- /// @}
-
- /// \name Block access optimizations
- // Between operations used in optimization of transpositions for regular vertices. Maybe we don't need these? TODO
- /// @{
- /** Return first after i, but before or equal j; return i if no such element found */
- const_reference first_between(const_reference i, const_reference j, const OrderComparison& cmp);
- /// Add lists and remove all elements after i and before or equal to j
- const_reference add_and_first_between(const Self& c, const ConsistencyComparison& consistency_cmp,
- const_reference i, const_reference j, const OrderComparison& order_cmp);
- /// Erase everything after i, but before or equal to j
- void erase_between(const_reference i, const_reference j, const OrderComparison& cmp);
- /// @}
-
- /// \name Debugging
- /// @{
- const_reference get_first(const OrderComparison& cmp) const; ///< First element in cmp order
- std::ostream& operator<<(std::ostream& out) const;
- /// @}
-
- private:
- typedef std::list<Item> TemporaryCycleRepresenation;
-
- using CycleRepresentation::push_back;
- using CycleRepresentation::insert;
-
- private:
- size_t sz;
-
- private:
- // Serialization
- typedef List<Item> Parent;
- friend class boost::serialization::access;
- template<class Archive>
- void serialize(Archive& ar, version_type );
-};
-
-#include "cycle.hpp"
-
-#endif // __CYCLE_H__
-
--- a/include/topology/cycle.hpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,280 +0,0 @@
-#include <algorithm>
-#include <vector>
-
-#include <boost/serialization/split_member.hpp>
-#include <boost/serialization/nvp.hpp>
-#include <boost/serialization/binary_object.hpp>
-#include <boost/utility.hpp>
-
-using boost::serialization::make_nvp;
-using boost::serialization::make_binary_object;
-
-template<class I, class OrderCmp, class ConsistencyCmp>
-Cycle<I,OrderCmp,ConsistencyCmp>::
-Cycle(): sz(0)
-{}
-
-template<class I, class OrderCmp, class ConsistencyCmp>
-Cycle<I,OrderCmp,ConsistencyCmp>::
-Cycle(const Cycle& c): CycleRepresentation(c), sz(c.sz)
-{}
-
-template<class I, class OrderCmp, class ConsistencyCmp>
-void
-Cycle<I,OrderCmp,ConsistencyCmp>::
-append(const_reference x, const ConsistencyCmp& cmp)
-{
- // First try the special cases that x goes at the end
- const_reference last = CycleRepresentation::back();
- if (empty() || cmp(last, x))
- {
- push_back(x);
- return;
- }
-
- for (iterator cur = begin(); cur != end(); ++cur)
- if (cmp(x, *cur))
- {
- insert(cur, x);
- return;
- }
-}
-
-template<class I, class OrderCmp, class ConsistencyCmp>
-typename Cycle<I,OrderCmp,ConsistencyCmp>::const_reference
-Cycle<I,OrderCmp,ConsistencyCmp>::
-top(const OrderComparison& cmp) const
-{
- AssertMsg(!empty(), "Cycle must not be empty for top()");
- const_iterator min = begin();
- for (const_iterator cur = ++begin(); cur != end(); ++cur)
- if (cmp(*cur, *min))
- min = cur;
- return *min;
-}
-
-template<class I, class OrderCmp, class ConsistencyCmp>
-void
-Cycle<I,OrderCmp,ConsistencyCmp>::
-swap(Cycle& c)
-{
- CycleRepresentation::swap(c);
- std::swap(sz, c.sz);
-}
-
-template<class I, class OrderCmp, class ConsistencyCmp>
-void
-Cycle<I,OrderCmp,ConsistencyCmp>::
-sort(const ConsistencyComparison& cmp)
-{
- std::vector<Item> tmp(begin(), end());
- std::sort(tmp.begin(), tmp.end(), cmp);
- std::copy(tmp.begin(), tmp.end(), begin());
-}
-
-template<class I, class OrderCmp, class ConsistencyCmp>
-typename Cycle<I,OrderCmp,ConsistencyCmp>::iterator
-Cycle<I,OrderCmp,ConsistencyCmp>::
-get_second(const OrderComparison& cmp) const
-{
- AssertMsg(!empty(), "Cycle must not be empty for get_second()");
- if (size() < 2) return begin(); // Indicates that there is no second.
-
- Dout(dc::cycle, "Looking for second");
- AssertMsg(size() >= 2, "Cycle must have at least two elements for get_second()");
- iterator min = begin();
- iterator second = ++begin();
- if (cmp(*second, *min))
- std::swap(min, second);
- for (iterator cur = boost::next(begin(),2); cur != end(); ++cur)
- {
- if (cmp(*cur, *min))
- {
- second = min;
- min = cur;
- } else if (cmp(*cur, *second))
- {
- second = cur;
- }
- }
-
- Dout(dc::cycle, "Looked up: " << **second);
- return second;
-}
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-typename Cycle<I,OrderCmp,ConsistencyCmp>::const_reference
-Cycle<I,OrderCmp,ConsistencyCmp>::
-first_between(const_reference i, const_reference j, const OrderComparison& cmp)
-{
- // Find the first element in ConsistencyComparison order (> i and <= j)
- const_pointer first = &i;
- iterator cur = begin();
- for (; cur != end(); ++cur)
- {
- if ((*cur == j) || (cmp(*cur, j) && cmp(i, *cur)))
- {
- first = &(*cur);
- break;
- }
- }
-
- // If no such element found, then we are done
- if (cur == end())
- return i;
-
- // Find first element in OrderComparison order (> i and <= j)
- for (++cur; cur != end(); ++cur)
- {
- if ((*cur == j) || (cmp(*cur, j) && cmp(i, *cur)))
- {
- if (cmp(*cur, *first))
- first = &(*cur);
- }
- }
- return *first;
-}
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-void
-Cycle<I,OrderCmp,ConsistencyCmp>::
-erase_between(const_reference i, const_reference j, const OrderComparison& cmp)
-{
- for (iterator cur = begin(); cur != end(); ++cur)
- while ((cur != end()) && ((*cur == j) || (cmp(*cur, j) && cmp(i, *cur))))
- {
- Dout(dc::cycle, "Iteration of the erase while loop");
- cur = erase(cur);
- }
-}
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-std::ostream&
-Cycle<I,OrderCmp,ConsistencyCmp>::
-operator<<(std::ostream& out) const
-{
- for (const_iterator cur = begin(); cur != end(); ++cur)
- {
- out << **cur << ", ";
- }
- // out << "(last: " << *last << ")"; // For debugging only
- return out;
-}
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-std::ostream&
-operator<<(std::ostream& out, const Cycle<I, OrderCmp, ConsistencyCmp>& c)
-{
- return c.operator<<(out);
-}
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-typename Cycle<I, OrderCmp, ConsistencyCmp>::Self&
-Cycle<I, OrderCmp, ConsistencyCmp>::
-add(const Self& c, const ConsistencyCmp& cmp)
-{
- Dout(dc::cycle, "Adding cycles: " << *this << " + " << c);
-
- iterator cur1 = begin();
- const_iterator cur2 = c.begin();
-
- while (cur2 != c.end())
- {
- if (cur1 == end())
- {
- while (cur2 != c.end())
- push_back(*cur2++);
- Dout(dc::cycle, "After addition: " << *this);
- return *this;
- }
-
- // mod 2
- int res = cmp.compare(*cur1, *cur2);
- Dout(dc::cycle, "Comparison result: " << res);
- if (res == 0) // *cur1 == *cur2
- {
- Dout(dc::cycle, "Equality");
- cur1 = erase(cur1); // erase cur1 --- as a result cur1 will be pointing at old_cur1++
- --sz;
- ++cur2;
- } else if (res < 0) // *cur1 < *cur2
- {
- Dout(dc::cycle, "Less than");
- cur1++;
- } else if (res > 0) // *cur1 > *cur2
- {
- Dout(dc::cycle, "Greater than");
- insert(cur1, *cur2);
- ++cur2;
- ++sz;
- }
- }
-
- Dout(dc::cycle, "After addition: " << *this);
- return *this;
-}
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-typename Cycle<I,OrderCmp,ConsistencyCmp>::const_reference
-Cycle<I,OrderCmp,ConsistencyCmp>::
-add_and_first_between(const Self& c, const ConsistencyComparison& consistency_cmp,
- const_reference i, const_reference j, const OrderComparison& order_cmp)
-{
- add(c, consistency_cmp);
- return first_between(i,j, order_cmp);
-}
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-typename Cycle<I,OrderCmp,ConsistencyCmp>::const_reference
-Cycle<I,OrderCmp,ConsistencyCmp>::
-get_first(const OrderComparison& cmp) const
-{ return top(cmp); }
-
-
-template<typename I, class OrderCmp, class ConsistencyCmp>
-template<class Archive>
-void
-Cycle<I,OrderCmp,ConsistencyCmp>::
-serialize(Archive& ar, version_type )
-{
- ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Parent);
- ar & make_nvp("size", sz);;
-}
-
-
-/*
-template<typename I, class Cmp>
-void
-Cycle<I, Cmp>::
-insertion_sort(const Comparison& cmp)
-{
- TemporaryCycleRepresenation tmp;
-
- // Insertion sort into the temporary list
- for (const_iterator cur = begin(); cur != end(); ++cur)
- {
- typename TemporaryCycleRepresenation::iterator tmp_cur = tmp.end();
- typename TemporaryCycleRepresenation::iterator tmp_next = tmp_cur--;
-
- while (tmp_next != tmp.begin())
- {
- if (cmp(*cur, *tmp_cur))
- tmp_next = tmp_cur--;
- else
- break;
- }
- tmp.insert(tmp_next, *cur);
- }
-
- // Copy temporary list back into ourselves
- iterator cur = begin();
- typename TemporaryCycleRepresenation::const_iterator tmp_cur = tmp.begin();
- while(tmp_cur != tmp.end())
- {
- *cur = *tmp_cur;
- ++cur; ++tmp_cur;
- }
-}
-*/
-
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/cycles.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,49 @@
+#ifndef __CYCLES_H__
+#define __CYCLES_H__
+
+#include "chain.h"
+#include "utilities/circular_list.h"
+
+#if DEBUG_CONTAINERS
+ #include <debug/vector>
+ #include <debug/deque>
+ using std::__debug::vector;
+ using std::__debug::deque;
+#else
+ #include <vector>
+ #include <deque>
+ using std::vector;
+ using std::deque;
+#endif
+
+template<class OrderIndex_ = int>
+struct VectorChains
+{
+ typedef OrderIndex_ OrderIndex;
+ typedef ChainWrapper<vector<OrderIndex> > Chain;
+
+ template<class U> struct rebind
+ { typedef VectorChains<U> other; };
+};
+
+template<class OrderIndex_ = int>
+struct DequeChains
+{
+ typedef OrderIndex_ OrderIndex;
+ typedef ChainWrapper<deque<OrderIndex> > Chain;
+
+ template<class U> struct rebind
+ { typedef DequeChains<U> other; };
+};
+
+template<class OrderIndex_ = int>
+struct ListChains
+{
+ typedef OrderIndex_ OrderIndex;
+ typedef ChainWrapper<List<OrderIndex> > Chain;
+
+ template<class U> struct rebind
+ { typedef ListChains<U> other; };
+};
+
+#endif // __CYCLES_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/dynamic-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,326 @@
+#ifndef __DYNAMIC_PERSISTENCE_H__
+#define __DYNAMIC_PERSISTENCE_H__
+
+#include "static-persistence.h"
+#include <utilities/types.h>
+
+#include <boost/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+namespace bl = boost::lambda;
+
+#ifdef COUNTERS
+static Counter* cTrailLength = GetCounter("persistence/pair/traillength"); // the size of matrix U in RU decomposition
+static Counter* cChainLength = GetCounter("persistence/pair/chainlength"); // the size of matrix V in R=DV decomposition
+#endif // COUNTERS
+
+template<class Data_, class ChainTraits_>
+struct TrailData: public PairCycleData<Data_, ChainTraits_, TrailData<Data_, ChainTraits_> >
+{
+ typedef Data_ Data;
+
+ typedef PairCycleData<Data_, ChainTraits_, TrailData> Parent;
+ typedef TrailData<Data_, ChainTraits_> Self;
+
+ typedef typename Parent::Index Index;
+ typedef typename Parent::Cycle Cycle;
+ typedef typename Parent::Chain Chain;
+ typedef Chain Trail;
+
+ // Modifiers
+ template<class Cmp>
+ void trail_append(Index i, const Cmp& cmp) { trail.append(i, cmp); }
+ template<class Cmp>
+ void trail_add(const Trail& t, const Cmp& cmp) { trail.add(t, cmp); }
+
+ template<class Cmp>
+ void cycle_add(const Cycle& z, const Cmp& cmp) { cycle.add(z, cmp); }
+
+ using Parent::cycle;
+ Trail trail;
+};
+
+/**
+ * Class: DynamicPersistenceTrails
+ * Derives from StaticPersistence and allows one to update persistence
+ * after a transposition of two contiguous simplices in a filtration.
+ * In addition to reduced cycles, it stores each OrderElement's trails,
+ * i.e. in addition to matrix R, it stores matrix U in vineyard notation.
+ *
+ * Template parameters:
+ * Data_ - auxilliary contents to store with each OrderElement
+ * OrderDescriptor_ - class describing how the order is stored; it defaults to <VectorOrderDescriptor>
+ * which serves as a prototypical class
+ */
+// TODO: perhaps Consistency should be wrapped into a ConsistencyDescriptor that somehow knows how to initialize it.
+// That way one could provide a simple consistency descriptor that just stored some integers describing the original
+// position, or one could provide consistency that is references into the complex
+template<class Data_ = Empty<>,
+ class ChainTraits_ = VectorChains<>,
+ class ContainerTraits_ = OrderConsistencyContainer<>,
+ class Element_ = TrailData<Data_, ChainTraits_>,
+ class Comparison_ = ElementComparison<typename ContainerTraits_::template rebind<Element_>::other::Container,
+ std::greater<typename ContainerTraits_::template rebind<Element_>::other::Container::iterator> >,
+ class ConsistencyComparison_ = ElementComparison<typename ContainerTraits_::template rebind<Element_>::other::ConsistentContainer,
+ std::greater<typename ContainerTraits_::template rebind<Element_>::other::ConsistentContainer::iterator> >
+ >
+class DynamicPersistenceTrails:
+ public StaticPersistence<Data_, ChainTraits_, ContainerTraits_, Element_, Comparison_>
+{
+ public:
+ typedef Data_ Data;
+ typedef Element_ Element;
+ typedef StaticPersistence<Data_, ChainTraits_,
+ ContainerTraits_, Element_, Comparison_> Parent;
+
+ typedef typename Parent::ContainerTraits Traits;
+ typedef typename Parent::Order Order;
+ typedef typename Traits::ConsistentContainer Consistency;
+ typedef typename Parent::OrderComparison OrderComparison;
+ typedef typename Parent::OrderIndex OrderIndex;
+ typedef ConsistencyComparison_ ConsistencyComparison;
+ typedef typename Parent::iterator iterator;
+
+ typedef typename Element::Trail Trail;
+ typedef typename Element::Cycle Cycle;
+
+ /* Constructor: DynamicPersistenceTrails() */
+ DynamicPersistenceTrails();
+
+ /**
+ * Constructor: DynamicPersistenceTrails()
+ * TODO: write a description
+ *
+ * Template parameters:
+ * Filtration - filtration of the complex whose persistence we are computing
+ */
+ template<class Filtration> DynamicPersistenceTrails(const Filtration& f);
+
+ template<class Filtration>
+ void initialize(const Filtration& f) { Parent::initialize(f); }
+
+ void pair_simplices();
+
+ // Function: transpose(i)
+ // Tranpose i and the next element.
+ // Returns: true iff the pairing switched.
+ template<class DimensionFunctor, class Visitor>
+ bool transpose(iterator i, const DimensionFunctor& dimension, Visitor visitor = Visitor());
+
+ template<class DimensionFunctor>
+ bool transpose(iterator i, const DimensionFunctor& dimension) { return transpose(i,dimension,TranspositionVisitor()); }
+
+ using Parent::begin;
+ using Parent::end;
+ using Parent::iterator_to;
+ using Parent::index;
+ using Parent::size;
+ using Parent::order_comparison;
+
+ template<class Iter>
+ void rearrange(Iter i);
+
+ // Struct: TranspositionVisitor
+ //
+ // For example, a VineardVisitor could implement this archetype.
+ struct TranspositionVisitor
+ {
+ // Function: transpose(i)
+ // This function is called before transposition is processed
+ // (at the very beginning of <transpose(i, visitor)>). It is meant to update any structures
+ // that may need to be updated, but perhaps it has other uses as well.
+ void transpose(iterator i) const {}
+
+ // Function: switched(i, type)
+ // This function is called after the transposition if the switch in pairing has occured.
+ // `i` is the index of the preceding simplex after the transposition.
+ // `type` indicates the <SwitchType>.
+ void switched(iterator i, SwitchType type) const {}
+ };
+
+ protected:
+ using Parent::order;
+ using Parent::set_pair;
+ using Parent::swap_cycle;
+
+ Consistency& consistent_order() { return order().template get<consistency>(); }
+ const Consistency& consistent_order() const { return order().template get<consistency>(); }
+
+ bool trail_remove_if_contains
+ (iterator i, OrderIndex j) { TrailRemover rm(j); order().modify(i, rm); return rm.result; }
+ void cycle_add(iterator i, const Cycle& z) { order().modify(i, boost::bind(&Element::template cycle_add<ConsistencyComparison>, bl::_1, boost::ref(z), ccmp_)); } // i->cycle_add(z, ccmp_)
+ void trail_add(iterator i, const Trail& t) { order().modify(i, boost::bind(&Element::template trail_add<ConsistencyComparison>, bl::_1, boost::ref(t), ccmp_)); } // i->trail_add(t, ccmp_)
+
+ private:
+ void swap(iterator i, iterator j);
+ void pairing_switch(iterator i, iterator j);
+
+ struct PairingTrailsVisitor: public Parent::PairVisitor
+ {
+ PairingTrailsVisitor(Order& order, ConsistencyComparison ccmp, unsigned size):
+ Parent::PairVisitor(size), order_(order), ccmp_(ccmp) {}
+
+ void init(iterator i) const { order_.modify(i, boost::bind(&Element::template trail_append<ConsistencyComparison>, bl::_1, &*i, ccmp_)); Count(cTrailLength); } // i->trail_append(&*i, ccmp)
+ void update(iterator j, iterator i) const { order_.modify(order_.iterator_to(*(i->pair)), boost::bind(&Element::template trail_append<ConsistencyComparison>, bl::_1, &*j, ccmp_)); Count(cTrailLength); } // i->pair->trail_append(&*j, ccmp)
+ void finished(iterator i) const { Parent::PairVisitor::finished(i); }
+
+ Order& order_;
+ ConsistencyComparison ccmp_;
+ };
+
+ struct TrailRemover;
+
+ ConsistencyComparison ccmp_;
+};
+
+/* Chains */
+template<class Data_, class ChainTraits_>
+struct ChainData: public PairCycleData<Data_, ChainTraits_, ChainData<Data_, ChainTraits_> >
+{
+ typedef Data_ Data;
+
+ typedef PairCycleData<Data_, ChainTraits_, ChainData> Parent;
+ typedef ChainData<Data_, ChainTraits_> Self;
+
+ typedef typename Parent::Index Index;
+ typedef typename Parent::Cycle Cycle;
+ typedef typename Parent::Chain Chain;
+ typedef Chain Trail;
+
+ // Modifiers
+ template<class Cmp>
+ void chain_append(Index i, const Cmp& cmp) { chain.append(i, cmp); }
+ template<class Cmp>
+ void chain_add(const Chain& c, const Cmp& cmp) { chain.add(c, cmp); }
+
+ template<class Cmp>
+ void cycle_add(const Cycle& z, const Cmp& cmp) { cycle.add(z, cmp); }
+
+ using Parent::cycle;
+ Chain chain;
+};
+
+/**
+ * Class: DynamicPersistenceChains
+ *
+ * TODO: below comment is incorrect; nothing dynamic about this yet.
+ * Derives from StaticPersistence and allows one to update persistence
+ * after a transposition of two contiguous simplices in a filtration.
+ * In addition to reduced cycles, it stores each OrderElement's chains,
+ * i.e. in addition to matrix R, it stores matrix V in vineyard notation.
+ *
+ * Template parameters:
+ * Data_ - auxilliary contents to store with each OrderElement
+ * OrderDescriptor_ - class describing how the order is stored; it defaults to <VectorOrderDescriptor>
+ * which serves as a prototypical class
+ */
+template<class Data_ = Empty<>,
+ class ChainTraits_ = VectorChains<>,
+ class ContainerTraits_ = OrderConsistencyContainer<>,
+ class Element_ = ChainData<Data_, ChainTraits_>,
+ class Comparison_ = ElementComparison<typename ContainerTraits_::template rebind<Element_>::other::Container,
+ std::greater<typename ContainerTraits_::template rebind<Element_>::other::Container::iterator> >,
+ class ConsistencyComparison_ = ElementComparison<typename ContainerTraits_::template rebind<Element_>::other::ConsistentContainer,
+ std::greater<typename ContainerTraits_::template rebind<Element_>::other::ConsistentContainer::iterator> >
+ >
+class DynamicPersistenceChains:
+ public StaticPersistence<Data_, ChainTraits_, ContainerTraits_, Element_, Comparison_>
+{
+ public:
+ typedef Data_ Data;
+ typedef Element_ Element;
+ typedef StaticPersistence<Data_, ChainTraits_,
+ ContainerTraits_, Element_, Comparison_> Parent;
+
+ typedef typename Parent::ContainerTraits Traits;
+ typedef typename Parent::Order Order;
+
+ typedef typename Parent::OrderComparison OrderComparison;
+ typedef typename Parent::OrderIndex OrderIndex;
+ typedef ConsistencyComparison_ ConsistencyComparison;
+ typedef typename Parent::iterator iterator;
+
+ typedef typename Element::Chain Chain;
+ typedef typename Element::Cycle Cycle;
+
+ /* Constructor: DynamicPersistenceChains() */
+ DynamicPersistenceChains();
+
+ /**
+ * Constructor: DynamicPersistenceChains()
+ * TODO: write a description
+ *
+ * Template parameters:
+ * Filtration - filtration of the complex whose persistence we are computing
+ */
+ template<class Filtration> DynamicPersistenceChains(const Filtration& f);
+
+ template<class Filtration>
+ void initialize(const Filtration& f) { Parent::initialize(f); }
+ void pair_simplices();
+
+ // Function: transpose(i)
+ // Tranpose i and the next element.
+ // Returns: true iff the pairing switched.
+ // TODO
+ //bool transpose(OrderIndex i) { return transpose(i, TranspositionVisitor()); }
+
+ // TODO: the main missing piece to be dynamic
+ //template<class Visitor>
+ //bool transpose(OrderIndex i, Visitor& visitor = Visitor());
+
+ using Parent::begin;
+ using Parent::end;
+ using Parent::iterator_to;
+ using Parent::index;
+ using Parent::size;
+
+ // Struct: TranspositionVisitor
+ //
+ // For example, a VineardVisitor could implement this archetype.
+ struct TranspositionVisitor
+ {
+ // Function: transpose(i)
+ // This function is called before transposition is processed
+ // (at the very beginning of <transpose(i, visitor)>). It is meant to update any structures
+ // that may need to be updated, but perhaps it has other uses as well.
+ void transpose(iterator i) const {}
+
+ // Function: switched(i, type)
+ // This function is called after the transposition if the switch in pairing has occured.
+ // `i` is the index of the preceding simplex after the transposition.
+ // `type` indicates the <SwitchType>.
+ void switched(iterator i, SwitchType type) const {}
+ };
+
+ protected:
+ using Parent::order;
+ using Parent::set_pair;
+ using Parent::swap_cycle;
+
+ void cycle_add(iterator i, const Cycle& z) { order().modify(i, boost::bind(&Element::template cycle_add<ConsistencyComparison>, bl::_1, boost::ref(z), ccmp_)); } // i->cycle_add(z, ccmp_)
+ void chain_add(iterator i, const Chain& c) { order().modify(i, boost::bind(&Element::template chain_add<ConsistencyComparison>, bl::_1, boost::ref(c), ccmp_)); } // i->chain_add(c, ccmp_)
+
+ private:
+ void swap(OrderIndex i, OrderIndex j);
+ void pairing_switch(OrderIndex i, OrderIndex j);
+
+ struct PairingChainsVisitor: public Parent::PairVisitor
+ {
+ PairingChainsVisitor(Order& order, ConsistencyComparison ccmp, unsigned size):
+ Parent::PairVisitor(size), order_(order), ccmp_(ccmp) {}
+
+ void init(iterator i) const { order_.modify(i, boost::bind(&Element::template chain_append<ConsistencyComparison>, bl::_1, &*i, ccmp_)); } // i->chain_append(&*i, ccmp)
+ void update(iterator j, iterator i) const { order_.modify(j, boost::bind(&Element::template chain_add<ConsistencyComparison>, bl::_1, i->pair->chain, ccmp_)); } // j->chain.add(i->pair->chain, ccmp_)
+ void finished(iterator i) const { Parent::PairVisitor::finished(i); CountBy(cChainLength, i->chain.size()); }
+
+ Order& order_;
+ ConsistencyComparison ccmp_;
+ };
+
+ ConsistencyComparison ccmp_;
+};
+
+
+#include "dynamic-persistence.hpp"
+
+#endif // __DYNAMIC_PERSISTENCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/dynamic-persistence.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,318 @@
+#include <utilities/log.h>
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlTranspositions = DEF_CHANNEL("topology/persistence/transpositions", rlog::Log_Debug);
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cTransposition = GetCounter("persistence/transposition");
+static Counter* cTranspositionDiffDim = GetCounter("persistence/transposition/diffdim");
+static Counter* cTranspositionCase12 = GetCounter("persistence/transposition/case/1/2");
+static Counter* cTranspositionCase12s = GetCounter("persistence/transposition/case/1/2/special");
+static Counter* cTranspositionCase112 = GetCounter("persistence/transposition/case/1/1/2");
+static Counter* cTranspositionCase111 = GetCounter("persistence/transposition/case/1/1/1");
+static Counter* cTranspositionCase22 = GetCounter("persistence/transposition/case/2/2");
+static Counter* cTranspositionCase212 = GetCounter("persistence/transposition/case/2/1/2");
+static Counter* cTranspositionCase211 = GetCounter("persistence/transposition/case/2/1/1");
+static Counter* cTranspositionCase32 = GetCounter("persistence/transposition/case/3/2");
+static Counter* cTranspositionCase31 = GetCounter("persistence/transposition/case/3/1");
+static Counter* cTranspositionCase4 = GetCounter("persistence/transposition/case/4");
+#endif // COUNTERS
+
+
+/* Trails */
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::
+DynamicPersistenceTrails():
+ ccmp_(consistent_order())
+{}
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+template<class Filtration>
+DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::
+DynamicPersistenceTrails(const Filtration& f):
+ Parent(f), ccmp_(consistent_order())
+{}
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+void
+DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::
+pair_simplices()
+{
+ PairingTrailsVisitor visitor(order(), ccmp_, size());
+ Parent::pair_simplices(begin(), end(), true, visitor);
+}
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+template<class DimensionFunctor, class Visitor>
+bool
+DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::
+transpose(iterator i, const DimensionFunctor& dimension, Visitor visitor)
+{
+#if LOGGING
+ typename Traits::OutputMap outmap(order());
+#endif
+
+ Count(cTransposition);
+ typedef typename Element::Trail::iterator TrailIterator;
+
+ visitor.transpose(i);
+
+ iterator i_prev = i++;
+
+ if (dimension(i_prev) != dimension(i))
+ {
+ swap(i_prev, i);
+ rLog(rlTranspositions, "Different dimension");
+ Count(cTranspositionDiffDim);
+ return false;
+ }
+
+ bool si = i_prev->sign(), sii = i->sign();
+ if (si && sii)
+ {
+ rLog(rlTranspositions, "Trail prev: %s", i_prev->trail.tostring(outmap).c_str());
+
+ // Case 1
+ if (trail_remove_if_contains(i_prev, index(i)))
+ rLog(rlTranspositions, "Case 1, U[i,i+1] = 1");
+
+ iterator k = iterator_to(i_prev->pair);
+ iterator l = iterator_to(i->pair);
+
+ // rLog(rlTranspositions, "(i_prev, k), (i, l): (%s, %s), (%s, %s)",
+ // outmap(i_prev).c_str(), outmap(k).c_str(),
+ // outmap(i).c_str(), outmap(l).c_str());
+
+ // Explicit treatment of unpaired simplex
+ if (l == i)
+ {
+ swap(i_prev, i);
+ rLog(rlTranspositions, "Case 1.2 --- unpaired");
+ rLog(rlTranspositions, "%s", outmap(i_prev).c_str());
+ Count(cTranspositionCase12);
+ return false;
+ } else if (k == i_prev)
+ {
+ if (!(l->cycle.contains(index(i_prev))))
+ {
+ // Case 1.2
+ swap(i_prev, i);
+ rLog(rlTranspositions, "Case 1.2 --- unpaired");
+ rLog(rlTranspositions, outmap(i_prev).c_str());
+ Count(cTranspositionCase12);
+ return false;
+ } else
+ {
+ // Case 1.2 --- special version (plain swap, but pairing switches)
+ swap(i_prev, i);
+ pairing_switch(i_prev, i);
+ visitor.switched(i, Case12);
+ rLog(rlTranspositions, "Case 1.2 --- unpaired (pairing switch)");
+ rLog(rlTranspositions, outmap(i_prev).c_str());
+ Count(cTranspositionCase12s);
+ return true;
+ }
+ }
+
+ rLog(rlTranspositions, "l cycle: %s", l->cycle.tostring(outmap).c_str());
+ if (!(l->cycle.contains(index(i_prev))))
+ {
+ // Case 1.2
+ rLog(rlTranspositions, "k is in l: %d", (bool) l->trail.contains(index(k))); // if true, a special update would be needed to maintain lazy decomposition
+ swap(i_prev, i);
+ rLog(rlTranspositions, "Case 1.2");
+ Count(cTranspositionCase12);
+ return false;
+ } else
+ {
+ // Case 1.1
+ if (std::not2(order_comparison())(index(k),index(l)))
+ {
+ // Case 1.1.1
+ swap(i_prev, i);
+ cycle_add(l, k->cycle); // Add column k to l
+ trail_add(k, l->trail); // Add row l to k
+ rLog(rlTranspositions, "Case 1.1.1");
+ Count(cTranspositionCase111);
+ return false;
+ } else
+ {
+ // Case 1.1.2
+ swap(i_prev, i);
+ cycle_add(k, l->cycle); // Add column l to k
+ trail_add(l, k->trail); // Add row k to l
+ pairing_switch(i_prev, i);
+ visitor.switched(i, Case112);
+ rLog(rlTranspositions, "Case 1.1.2");
+ Count(cTranspositionCase112);
+ return true;
+ }
+ }
+ } else if (!si && !sii)
+ {
+ // Case 2
+ if (!(i_prev->trail.contains(index(i))))
+ {
+ // Case 2.2
+ swap(i_prev, i);
+ rLog(rlTranspositions, "Case 2.2");
+ Count(cTranspositionCase22);
+ return false;
+ } else
+ {
+ // Case 2.1
+ iterator low_i = iterator_to(i_prev->pair);
+ iterator low_ii = iterator_to(i->pair);
+ trail_add(i_prev, i->trail); // Add row i to i_prev
+ cycle_add(i, i_prev->cycle); // Add column i_prev to i
+ swap(i_prev, i);
+ if (std::not2(order_comparison())(index(low_ii), index(low_i)))
+ {
+ // Case 2.1.2
+ cycle_add(i_prev, i->cycle); // Add column i to i_prev (after transposition)
+ trail_add(i, i_prev->trail); // Add row i to i_prev
+ pairing_switch(i_prev, i);
+ visitor.switched(i, Case212);
+ rLog(rlTranspositions, "Case 2.1.2");
+ Count(cTranspositionCase212);
+ return true;
+ }
+
+ // Case 2.1.1
+ rLog(rlTranspositions, "Case 2.1.1");
+ Count(cTranspositionCase211);
+ return false;
+ }
+ } else if (!si && sii)
+ {
+ // Case 3
+ if (!(i_prev->trail.contains(index(i))))
+ {
+ // Case 3.2
+ swap(i_prev, i);
+ rLog(rlTranspositions, "Case 3.2");
+ Count(cTranspositionCase32);
+ return false;
+ } else
+ {
+ // Case 3.1
+ trail_add(i_prev, i->trail); // Add row i to i_prev
+ cycle_add(i, i_prev->cycle); // Add column i_prev to i
+ swap(i_prev, i);
+ cycle_add(i_prev, i->cycle); // Add column i_prev to i (after transposition)
+ trail_add(i, i_prev->trail); // Add row i to i_prev
+ pairing_switch(i_prev, i);
+ visitor.switched(i, Case31);
+ rLog(rlTranspositions, "Case 3.1");
+ Count(cTranspositionCase31);
+ return true;
+ }
+ } else if (si && !sii)
+ {
+ // Case 4
+ if (trail_remove_if_contains(i_prev, index(i)))
+ rLog(rlTranspositions, "Case 4, U[i,i+1] = 1");
+ swap(i_prev, i);
+ rLog(rlTranspositions, "Case 4");
+ Count(cTranspositionCase4);
+ return false;
+ }
+
+ return false; // to avoid compiler complaints; we should never reach this point
+}
+
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+template<class Iter>
+void
+DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::
+rearrange(Iter i)
+{
+ order().rearrange(i);
+ consistent_order().rearrange(i);
+
+ // Resort the cycles
+ Cycle z;
+ for(iterator i = begin(); i != end(); ++i)
+ {
+ Parent::swap_cycle(i, z);
+ z.sort(ccmp_);
+ Parent::swap_cycle(i, z);
+ }
+}
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+void
+DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::
+swap(iterator i, iterator j)
+{
+ order().relocate(i,j);
+}
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+void
+DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::
+pairing_switch(iterator i, iterator j)
+{
+ OrderIndex i_pair = i->pair;
+ OrderIndex j_pair = j->pair;
+
+ // rLog(rlTranspositions, " (%x %x %x) (%x %x %x)", &*i, i->pair, i->pair->pair, &*j, j->pair, j->pair->pair);
+ if (i_pair == index(i))
+ set_pair(j, j);
+ else
+ {
+ set_pair(j, i_pair);
+ set_pair(i_pair, j);
+ }
+
+ if (j_pair == index(j))
+ set_pair(i, i);
+ else
+ {
+ set_pair(i, j_pair);
+ set_pair(j_pair, i);
+ }
+ // rLog(rlTranspositions, " (%x %x %x) (%x %x %x)", &*i, i->pair, i->pair->pair, &*j, j->pair, j->pair->pair);
+}
+
+// Helper classes
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+struct DynamicPersistenceTrails<D,CT,OT,E,Cmp,CCmp>::TrailRemover:
+ public std::unary_function<Element&, void>
+{
+ TrailRemover(OrderIndex i):
+ i_(i) {}
+
+ void operator()(Element& e) { result = e.trail.remove_if_contains(i_); }
+
+ OrderIndex i_;
+ bool result;
+};
+
+
+/* Chains */
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+DynamicPersistenceChains<D,CT,OT,E,Cmp,CCmp>::
+DynamicPersistenceChains():
+ ccmp_(order().template get<consistency>())
+{}
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+template<class Filtration>
+DynamicPersistenceChains<D,CT,OT,E,Cmp,CCmp>::
+DynamicPersistenceChains(const Filtration& f):
+ Parent(f), ccmp_(order().template get<consistency>())
+{}
+
+template<class D, class CT, class OT, class E, class Cmp, class CCmp>
+void
+DynamicPersistenceChains<D,CT,OT,E,Cmp,CCmp>::
+pair_simplices()
+{
+ PairingChainsVisitor visitor(order(), ccmp_, size());
+ Parent::pair_simplices(begin(), end(), true, visitor);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/field-arithmetic.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,68 @@
+#ifndef __FIELD_ARITHMETIC_H__
+#define __FIELD_ARITHMETIC_H__
+
+#include <vector>
+
+class ZpField
+{
+ public:
+ typedef int Element;
+
+ ZpField(Element p = 2);
+
+ Element id() const { return 1; }
+ Element zero() const { return 0; }
+ Element init(int a) const { return (a % p_ + p_) % p_; }
+
+ Element neg(Element a) const { return p_ - a; }
+ Element add(Element a, Element b) const { return (a+b) % p_; }
+
+ Element inv(Element a) const { return inverses_[a]; }
+ Element mul(Element a, Element b) const { return (a*b) % p_; }
+ Element div(Element a, Element b) const { return mul(a, inv(b)); }
+
+ bool is_zero(Element a) const { return (a % p_) == 0; }
+
+ private:
+ Element p_;
+ std::vector<Element> inverses_;
+};
+
+ZpField::
+ZpField(Element p):
+ p_(p), inverses_(p_)
+{
+ for (Element i = 1; i < p_; ++i)
+ for (Element j = 1; j < p_; ++j)
+ if (mul(i,j) == 1)
+ {
+ inverses_[i] = j;
+ break;
+ }
+}
+
+#if 0 // unused example; commented out to get rid of the artificial dependence on GMP
+#include <gmpxx.h>
+
+class QField
+{
+ public:
+ typedef mpq_class Element;
+
+ QField() {}
+
+ Element id() const { return 1; }
+ Element zero() const { return 0; }
+
+ Element neg(Element a) const { return -a; }
+ Element add(Element a, Element b) const { return (a+b); }
+
+ Element inv(Element a) const { return id()/a; }
+ Element mul(Element a, Element b) const { return (a*b); }
+ Element div(Element a, Element b) const { return a/b; }
+
+ bool is_zero(Element a) const { return a == 0; }
+};
+#endif
+
+#endif // __FIELD_ARITHMETIC_H__
--- a/include/topology/filtration.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/filtration.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,135 +1,156 @@
-/*
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
- */
-
#ifndef __FILTRATION_H__
#define __FILTRATION_H__
-#include "utilities/sys.h"
-#include "utilities/debug.h"
+#include <vector>
+#include <iostream>
+
+#include "complex-traits.h"
-#include "filtrationcontainer.h"
-#include "filtrationsimplex.h"
-#include "vineyard.h"
+#include "utilities/indirect.h"
+#include "utilities/property-maps.h"
+#include "utilities/types.h"
-#include <map>
-#include <vector>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/random_access_index.hpp>
#include <boost/serialization/access.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/serialization.hpp>
-/**
- * Filtration class. Serves as an (ordered) container for the simplices,
- * and provides pair_simplices() method that computes the RU-decomposition
- * for the simplex order stored in the filtration. Iterators remain valid
- * through all the operations.
- */
-template<class Smplx, class FltrSmplx = FiltrationSimplex<Smplx>, class Vnrd = Vineyard<FltrSmplx> >
-class Filtration: public FltrSmplx::Container
+
+namespace b = boost;
+namespace bmi = boost::multi_index;
+
+
+// Class: Filtration
+//
+// Filtration keeps track of the ordering of the simplices in a complex.
+// The most significant function it provides is <boundary()> which converts
+// the boundary of a simplex at a given index into a list of indices.
+template<class Simplex_,
+ class SimplexOrderIndex_ = bmi::ordered_unique<bmi::identity<Simplex_>,
+ typename Simplex_::VertexComparison> >
+class Filtration
{
- public:
- typedef Smplx Simplex;
- typedef FltrSmplx FiltrationSimplex;
- typedef Vnrd Vineyard;
-
- /// \name Container Types
- /// @{
- /** The actual container type (which is the parent of the Filtration) */
- typedef typename FiltrationSimplex::Container FiltrationContainer;
- typedef typename FiltrationContainer::Index Index;
- typedef typename FiltrationContainer::const_Index const_Index;
- typedef Index iterator;
- typedef const_Index const_iterator;
- /// @}
-
- /// \name Cycles and Trails
- /// @{
- typedef typename FiltrationContainer::GreaterThanComparison CyclesComparator;
- typedef typename FiltrationContainer::LessThanComparison TrailsComparator;
- typedef typename FiltrationContainer::ConsistencyComparison ConsistencyComparator;
- typedef typename FiltrationContainer::Cycle Cycle;
- typedef typename FiltrationContainer::Trail Trail;
- typedef typename Cycle::iterator CycleIterator;
- typedef typename Trail::iterator TrailIterator;
- /// @}
-
- typedef Filtration<Simplex, FiltrationSimplex, Vineyard> Self;
- typedef FiltrationContainer Parent;
+ private:
+ struct order {}; // tag
+
+ public:
+ // Typedefs: Template parameters
+ typedef Simplex_ Simplex;
+ typedef SimplexOrderIndex_ SimplexOrderIndex;
+
+ typedef b::multi_index_container<Simplex,
+ bmi::indexed_by<SimplexOrderIndex,
+ bmi::random_access<bmi::tag<order> >
+ >
+ > Container;
+ typedef typename Container::value_type value_type;
+
+ // Typedefs: Complex and Order views
+ typedef typename Container::template nth_index<0>::type Complex;
+ typedef typename Container::template nth_index<1>::type Order;
+ typedef typename Order::const_iterator Index;
+
+ Filtration() {}
+
+ // Constructor: Filtration(bg, end, cmp)
+ template<class ComplexIndex>
+ Filtration(ComplexIndex bg, ComplexIndex end):
+ container_(bg, end) {}
+
+ // Constructor: Filtration(bg, end, cmp)
+ template<class ComplexIndex, class Comparison>
+ Filtration(ComplexIndex bg, ComplexIndex end, const Comparison& cmp = Comparison()):
+ container_(bg, end) { sort(cmp); }
+
+ // Lookup
+ const Simplex& simplex(Index i) const { return *i; }
+ Index find(const Simplex& s) const { return bmi::project<order>(container_, container_.find(s)); }
+
+ // Modifiers
+ template<class Comparison>
+ void sort(const Comparison& cmp = Comparison()) { container_.template get<order>().sort(cmp); }
+ void push_back(const Simplex& s) { container_.template get<order>().push_back(s); }
+ void transpose(Index i) { container_.template get<order>().relocate(i, i+1); }
+ void clear() { container_.template get<order>().clear(); }
+ template<class Iter>
+ void rearrange(Iter i) { container_.template get<order>().rearrange(i); }
+
+ Index begin() const { return container_.template get<order>().begin(); }
+ Index end() const { return container_.template get<order>().end(); }
+ size_t size() const { return container_.size(); }
- public:
- Filtration(Vineyard* vineyard);
-
- /// \name Core Functionality
- /// @{
- /// Computes RU decomposition of the simplices in [bg, end) range, assuming that everything before bg has been paired
- void pair_simplices(Index bg, Index end);
- void pair_simplices() { pair_simplices(begin(), end()); }
- bool transpose(Index i, bool maintain_lazy = true);
- bool is_paired() const;
- Index append(const Simplex& s); ///< Appends s to the filtration
- Index insert(Index prior, const Simplex& s); ///< Inserts s after prior
- const_Index get_index(const Simplex& s) const; /**< Returns the iterator pointing to s
- (end() if s not in filtration) */
- Index get_index(const Simplex& s); ///< \overload
- void fill_simplex_index_map(); ///< Fills the mapping for get_index()
- /// @}
-
- /// \name Accessors
- /// @{
- Vineyard* vineyard() { return vineyard_; }
- const Vineyard* vineyard() const { return vineyard_; }
- /// @}
-
- protected:
- using Parent::swap;
- bool transpose_simplices(Index i, bool maintain_lazy);
+ std::ostream& operator<<(std::ostream& out) const { std::copy(begin(), end(), std::ostream_iterator<Simplex>(out, "\n")); return out; }
+
+ private:
+ Container container_;
+
+ private:
+ // Serialization
+ friend class boost::serialization::access;
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int)
+ { ar & boost::serialization::make_nvp("order", container_); }
+};
+
+template<class S, class SOI>
+std::ostream&
+operator<<(std::ostream& out, const Filtration<S,SOI>& f) { return f.operator<<(out); }
+
+
+template<class Functor_, class Filtration_>
+class ThroughFiltration
+{
+ public:
+ typedef Filtration_ Filtration;
+ typedef Functor_ Functor;
+
+ typedef typename Functor::result_type result_type;
+ typedef typename Filtration::Index first_argument_type;
+
+ ThroughFiltration(const Filtration& filtration,
+ const Functor& functor):
+ filtration_(filtration),
+ functor_(functor) {}
- public:
- /// \name Container Operations
- /// @{
- using Parent::size;
- using Parent::begin;
- using Parent::end;
- /// @}
-
- std::ostream& operator<<(std::ostream& out) const;
+ result_type operator()(first_argument_type a) const { return functor_(filtration_.simplex(a)); }
- protected:
- /// \name Comparator accessors (protected)
- /// @{
- const ConsistencyComparator& get_consistency_cmp() const { return consistency_cmp; }
- const CyclesComparator& get_cycles_cmp() const { return cycles_cmp; }
- const TrailsComparator& get_trails_cmp() const { return trails_cmp; }
- /// @}
+ private:
+ const Filtration& filtration_;
+ const Functor& functor_;
+};
- private:
- typedef std::map<Simplex, Index> SimplexMap;
+template<class Filtration, class Functor>
+ThroughFiltration<Functor, Filtration>
+evaluate_through_filtration(const Filtration& filtration, const Functor& functor)
+{ return ThroughFiltration<Functor, Filtration>(filtration, functor); }
+
- /// Initializes the cycle with the indices of the simplices in the boundary, and the trail with the index of this simplex
- void init_cycle_trail(Index j);
- void pairing_switch(Index i, Index j);
-
- bool paired;
- SimplexMap inverse_simplices;
+template<class Map, class Filtration>
+class DimensionFunctor
+{
+ public:
+ DimensionFunctor(const Map& map, const Filtration& filtration):
+ map_(map), filtration_(filtration)
+ {}
- Vineyard* vineyard_;
+ template<class key_type>
+ Dimension operator()(key_type i) const { return filtration_.simplex(map_[i]).dimension(); }
- CyclesComparator cycles_cmp;
- TrailsComparator trails_cmp;
- ConsistencyComparator consistency_cmp;
+ private:
+ const Map& map_;
+ const Filtration& filtration_;
+};
- private:
- /* Serialization */
- friend class boost::serialization::access;
-
- typedef std::map<const_Index, SizeType, ConsistencyComparator> IndexIntMap;
- typedef std::vector<Index> IndexVector;
-
- template<class Archive> void save(Archive& ar, version_type ) const;
- template<class Archive> void load(Archive& ar, version_type );
- BOOST_SERIALIZATION_SPLIT_MEMBER()
-};
+template<class Map, class Filtration>
+DimensionFunctor<Map, Filtration>
+make_dimension_functor(const Map& map, const Filtration& filtration)
+{ return DimensionFunctor<Map, Filtration>(map, filtration); }
+
#include "filtration.hpp"
-#endif // __FILTRATION_H__
+#endif // __FILTRATION_H__
--- a/include/topology/filtration.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/filtration.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,463 +1,6 @@
-#include "utilities/counter.h"
-#include "utilities/types.h"
-#include <algorithm>
-
-#include <boost/utility.hpp>
-#include <boost/serialization/vector.hpp>
-#include <boost/serialization/nvp.hpp>
-#include <boost/serialization/list.hpp>
-#include <boost/serialization/is_abstract.hpp>
-
-using boost::serialization::make_nvp;
-
-/* Filtration Public */
-
-template<class S, class FS, class V>
-Filtration<S, FS, V>::
-Filtration(Vineyard* vnrd = 0): paired(false), vineyard_(vnrd)
-{}
-
-template<class S, class FS, class V>
-void
-Filtration<S, FS, V>::
-pair_simplices(Index bg, Index end)
-{
- Dout(dc::filtration, "Entered: compute_pairing");
- for (Index j = bg; j != end; ++j)
- {
- Dout(dc::filtration|flush_cf|continued_cf, *j << ": ");
- init_cycle_trail(j);
- Cycle& bdry = j->cycle();
- Dout(dc::finish, bdry);
-
- CountNum("Boundaries", j->dimension());
- Count("SimplexCount");
-
- while(!bdry.empty())
- {
- Index i = bdry.top(cycles_cmp);
- Dout(dc::filtration, *i << ": " << *(i->pair()));
- AssertMsg(!cycles_cmp(i, j), "Simplices in the cycle must precede current simplex: " <<
- "(" << *i << " in cycle of " << *j << ")");
-
- // i is not paired, so we pair j with i
- if (i->pair() == i)
- {
- Dout(dc::filtration, "Pairing " << *i << " and " << *j << " with cycle " << j->cycle());
- i->set_pair(j);
- j->set_pair(i);
- CountNum("DepositedCycleLength", j->cycle().size());
- break;
- }
-
- // continue searching --- change the Dout to the continued mode with newlines FIXME
- Dout(dc::filtration, " Adding: [" << bdry << "] + ");
- Dout(dc::filtration, " [" << i->pair()->cycle() << "]");
- bdry.add(i->pair()->cycle(), get_consistency_cmp());
- i->pair()->trail().append(j, get_consistency_cmp());
- Dout(dc::filtration, "After addition: " << bdry);
- }
- Dout(dc::filtration, "Finished with " << *j << ": " << *(j->pair()));
- }
- paired = true;
-}
-
-template<class S, class FS, class V>
-bool
-Filtration<S, FS, V>::
-is_paired() const
-{ return paired; }
-
-/**
- * Transposes simplices at i and i+1, and records the knee in the vineyard if there is a change in pairing.
- * Returns true if the pairing changed.
- */
-template<class S, class FS, class V>
-bool
-Filtration<S,FS,V>::
-transpose(Index i, bool maintain_lazy)
-{
- AssertMsg(vineyard() != 0, "We must have a vineyard for transpositions");
-
- Index i_orig = i++;
-
- AssertMsg(i_orig->pair() != i, "Transposing simplices must not be paired");
- bool result = transpose_simplices(i_orig, maintain_lazy);
- AssertMsg(i_orig == boost::next(i), "Wrong indices after transposition");
-
- if (result) vineyard()->switched(i, i_orig);
- return result;
-}
-
-template<class S, class FS, class V>
-typename Filtration<S, FS, V>::Index
-Filtration<S, FS, V>::
-append(const Simplex& s)
-{
- Index i = push_back(FiltrationSimplex(s));
- return i;
-}
-
-template<class S, class FS, class V>
-typename Filtration<S, FS, V>::Index
-Filtration<S, FS, V>::
-insert(Index prior, const Simplex& s)
-{
- Index i = Parent::insert(prior, FiltrationSimplex(s));
- paired = false;
-
- return i;
-}
-
-template<class S, class FS, class V>
-typename Filtration<S, FS, V>::const_Index
-Filtration<S, FS, V>::
-get_index(const Simplex& s) const
-{
- typename SimplexMap::const_iterator i = inverse_simplices.find(s);
- if (i == inverse_simplices.end())
- return end();
- else
- return i->second;
-}
-
-template<class S, class FS, class V>
-typename Filtration<S, FS, V>::Index
-Filtration<S, FS, V>::
-get_index(const Simplex& s)
-{
- typename SimplexMap::const_iterator i = inverse_simplices.find(s);
- if (i == inverse_simplices.end())
- return end();
- else
- return i->second;
-}
-
-template<class S, class FS, class V>
-void
-Filtration<S, FS, V>::
-fill_simplex_index_map()
-{
- for (Index i = begin(); i != end(); ++i)
- inverse_simplices[*i] = i;
-}
-
-template<class S, class FS, class V>
-std::ostream&
-Filtration<S, FS, V>::
-operator<<(std::ostream& out) const
-{
- out << "Pairing: " << std::endl;
- for (const_Index i = begin(); i != end(); ++i)
- {
- out << "(" << *i << ", " << *(i->pair()) << "): ";
- out << i->cycle() << std::endl;
- }
- out << std::endl << std::endl;
-
- return out;
-}
-
-
-/* Filtration Protected */
-/// Transposes simplices at i and i+1. Returns true if the pairing switched.
-template<class S, class FS, class V>
-bool
-Filtration<S,FS,V>::
-transpose_simplices(Index i, bool maintain_lazy)
-{
- AssertMsg(is_paired(), "Pairing must be computed before transpositions");
- Count("SimplexTransposition");
-
- Index i_prev = i++;
-
- if (i_prev->dimension() != i->dimension())
- {
- swap(i_prev, i);
- Dout(dc::transpositions, "Different dimension");
- Count("Case DiffDim");
- return false;
- }
-
- bool si = i_prev->sign(), sii = i->sign();
- if (si && sii)
- {
- Dout(dc::transpositions, "Trail prev: " << i_prev->trail());
-
- // Case 1
- TrailIterator i_in_i_prev = std::find(i_prev->trail().begin(), i_prev->trail().end(), i);
- if (i_in_i_prev != i_prev->trail().end())
- {
- Dout(dc::transpositions, "Case 1, U[i,i+1] = 1");
- i_prev->trail().erase(i_in_i_prev);
- }
-
- Index k = i_prev->pair();
- Index l = i->pair();
+#include <utilities/log.h>
- // Explicit treatment of unpaired simplex
- if (l == i)
- {
- swap(i_prev, i);
- Dout(dc::transpositions, "Case 1.2 --- unpaired");
- Dout(dc::transpositions, *i_prev);
- Count("Case 1.2");
- return false;
- } else if (k == i_prev)
- {
- if (std::find(l->cycle().begin(), l->cycle().end(), i_prev) == l->cycle().end())
- {
- // Case 1.2
- swap(i_prev, i);
- Dout(dc::transpositions, "Case 1.2 --- unpaired");
- Dout(dc::transpositions, *i_prev);
- Count("Case 1.2");
- return false;
- } else
- {
- // Case 1.1.2 --- special version (plain swap, but pairing switches)
- swap(i_prev, i);
- pairing_switch(i_prev, i);
- Dout(dc::transpositions, "Case 1.1.2 --- unpaired");
- Dout(dc::transpositions, *i_prev);
- Count("Case 1.1.2");
- return true;
- }
- }
-
- Dout(dc::transpositions, "l cycle: " << l->cycle());
- if (std::find(l->cycle().begin(), l->cycle().end(), i_prev) == l->cycle().end())
- {
- // Case 1.2
- if (maintain_lazy)
- {
- TrailIterator k_in_l = std::find(l->trail().begin(), l->trail().end(), k);
- if (k_in_l != l->trail().end())
- {
- l->trail().add(k->trail(), Filtration::get_consistency_cmp()); // Add row k to l
- k->cycle().add(l->cycle(), Filtration::get_consistency_cmp()); // Add column l to k
- }
- }
- swap(i_prev, i);
- Dout(dc::transpositions, "Case 1.2");
- Count("Case 1.2");
- return false;
- } else
- {
- // Case 1.1
- if (trails_cmp(k,l))
- {
- // Case 1.1.1
- swap(i_prev, i);
- l->cycle().add(k->cycle(), Filtration::get_consistency_cmp()); // Add column k to l
- k->trail().add(l->trail(), Filtration::get_consistency_cmp()); // Add row l to k
- Dout(dc::transpositions, "Case 1.1.1");
- Count("Case 1.1.1");
- return false;
- } else
- {
- // Case 1.1.2
- swap(i_prev, i);
- k->cycle().add(l->cycle(), Filtration::get_consistency_cmp()); // Add column l to k
- l->trail().add(k->trail(), Filtration::get_consistency_cmp()); // Add row k to l
- pairing_switch(i_prev, i);
- Dout(dc::transpositions, "Case 1.1.2");
- Count("Case 1.1.2");
- return true;
- }
- }
- } else if (!si && !sii)
- {
- // Case 2
- if (std::find(i_prev->trail().begin(), i_prev->trail().end(), i) == i_prev->trail().end())
- {
- // Case 2.2
- swap(i_prev, i);
- Dout(dc::transpositions, "Case 2.2");
- Count("Case 2.2");
- return false;
- } else
- {
- // Case 2.1
- Index low_i = i_prev->pair();
- Index low_ii = i->pair();
- i_prev->trail().add(i->trail(), Filtration::get_consistency_cmp()); // Add row i to i_prev
- i->cycle().add(i_prev->cycle(), Filtration::get_consistency_cmp()); // Add column i_prev to i
- swap(i_prev, i);
- if (Filtration::get_trails_cmp()(low_ii, low_i))
- {
- // Case 2.1.2
- i_prev->cycle().add(i->cycle(), Filtration::get_consistency_cmp()); // Add column i to i_prev (after transposition)
- i->trail().add(i_prev->trail(), Filtration::get_consistency_cmp()); // Add row i to i_prev
- pairing_switch(i_prev, i);
- Dout(dc::transpositions, "Case 2.1.2");
- Count("Case 2.1.2");
- return true;
- }
-
- // Case 2.1.1
- Dout(dc::transpositions, "Case 2.1.1");
- Count("Case 2.1.1");
- return false;
- }
- } else if (!si && sii)
- {
- // Case 3
- if (std::find(i_prev->trail().begin(), i_prev->trail().end(), i) == i_prev->trail().end())
- {
- // Case 3.2
- swap(i_prev, i);
- Dout(dc::transpositions, "Case 3.2");
- Count("Case 3.2");
- return false;
- } else
- {
- // Case 3.1
- i_prev->trail().add(i->trail(), Filtration::get_consistency_cmp()); // Add row i to i_prev
- i->cycle().add(i_prev->cycle(), Filtration::get_consistency_cmp()); // Add column i_prev to i
- swap(i_prev, i);
- i_prev->cycle().add(i->cycle(), Filtration::get_consistency_cmp()); // Add column i_prev to i (after transposition)
- i->trail().add(i_prev->trail(), Filtration::get_consistency_cmp()); // Add row i to i_prev
- pairing_switch(i_prev, i);
- Dout(dc::transpositions, "Case 3.1");
- Count("Case 3.1");
- return true;
- }
- } else if (si && !sii)
- {
- // Case 4
- TrailIterator i_in_i_prev = std::find(i_prev->trail().begin(), i_prev->trail().end(), i);
- if (i_in_i_prev != i_prev->trail().end())
- {
- Dout(dc::transpositions, "Case 4, U[i,i+1] = 1");
- i_prev->trail().erase(i_in_i_prev);
- }
- swap(i_prev, i);
- Dout(dc::transpositions, "Case 4");
- Count("Case 4");
- return false;
- }
-
- return false; // to avoid compiler complaints, should never reach this point
-}
-
-
-/* Filtration Private */
-template<class S, class FS, class V>
-void
-Filtration<S, FS, V>::
-init_cycle_trail(Index j)
-{
- typename Simplex::Cycle bdry = j->boundary();
-
- for (typename Simplex::Cycle::const_iterator cur = bdry.begin(); cur != bdry.end(); ++cur)
- {
- Dout(dc::filtration, "Appending in init_cycle_trail(): " << *cur);
- AssertMsg(get_index(*cur) != end(), "Non-existent simplex in the cycle");
- j->cycle().append(get_index(*cur), get_consistency_cmp());
- }
- j->trail().append(j, get_consistency_cmp());
- j->set_pair(j);
-}
-
-/// Update the pairing, so that whoever was paired with i is now paired with j and vice versa.
-template<class S, class FS, class V>
-void
-Filtration<S,FS,V>::
-pairing_switch(Index i, Index j)
-{
- Index i_pair = i->pair();
- Index j_pair = j->pair();
-
- if (i_pair == i)
- j->set_pair(j);
- else
- {
- j->set_pair(i_pair);
- i_pair->set_pair(j);
- }
-
- if (j_pair == j)
- i->set_pair(i);
- else
- {
- i->set_pair(j_pair);
- j_pair->set_pair(i);
- }
-}
-
-/* Serialization */
-template<class S, class FS, class V>
-template<class Archive>
-void
-Filtration<S, FS, V>::
-save(Archive& ar, version_type ) const
-{
- ar << BOOST_SERIALIZATION_NVP(paired);
- ar << BOOST_SERIALIZATION_NVP(cycles_cmp);
- ar << BOOST_SERIALIZATION_NVP(trails_cmp);
- ar << BOOST_SERIALIZATION_NVP(consistency_cmp);
-
- SizeType sz = size();
- ar << make_nvp("size", sz);
- Dout(dc::filtration, "Size: " << sz);
-
- /* Record integer indices */
- IndexIntMap index_map; SizeType i = 0;
- for (const_Index cur = begin(); cur != end(); ++cur)
- { index_map[cur] = i++; }
-
- /* Save the simplices */
- int count = 0;
- for (const_Index cur = begin(); cur != end(); ++cur)
- {
- count++;
- // FIXME
- //FiltrationSimplexSerialization simplex = FiltrationSimplexSerialization(*cur, index_map);
- //ar << make_nvp("FiltrationSimplex", simplex);
- }
- Dout(dc::filtration, count << " simplices serialized");
-}
-
-template<class S, class FS, class V>
-template<class Archive>
-void
-Filtration<S, FS, V>::
-load(Archive& ar, version_type )
-{
- Dout(dc::filtration, "Starting to read filtration");
- ar >> BOOST_SERIALIZATION_NVP(paired);
- ar >> BOOST_SERIALIZATION_NVP(cycles_cmp);
- ar >> BOOST_SERIALIZATION_NVP(trails_cmp);
- ar >> BOOST_SERIALIZATION_NVP(consistency_cmp);
- Dout(dc::filtration, "In Filtration: first block read");
-
- SizeType sz;
- ar >> make_nvp("size", sz);
- Dout(dc::filtration, "In Filtration: size read " << sz);
-
- IndexVector index_vector(sz);
- for (SizeType i = 0; i < sz; ++i)
- {
- index_vector[i] = append(Simplex());
- }
-
- int count = 0;
- for (SizeType i = 0; i < sz; ++i)
- {
- // FIXME
- //FiltrationSimplexSerialization simplex;
- //ar >> make_nvp("FiltrationSimplex", simplex);
- count++;
- Dout(dc::filtration, "In Filtration: simplex read (" << count << ")");
- //simplex.set_filtration_simplex(*index_vector[i], index_vector);
- }
- Dout(dc::filtration, "In Filtration: simplices read");
-}
-
-template<class S, class FS, class V>
-std::ostream&
-operator<<(std::ostream& out, const Filtration<S, FS, V>& f)
-{ return f.operator<<(out); }
-
-
+#ifdef LOGGING
+static rlog::RLogChannel* rlFiltration = DEF_CHANNEL("topology/filtration/info", rlog::Log_Debug);
+static rlog::RLogChannel* rlFiltrationDebug = DEF_CHANNEL("topology/filtration/debug", rlog::Log_Debug);
+#endif // LOGGING
--- a/include/topology/filtrationcontainer.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2006
- */
-
-#ifndef __FILTRATIONCONTAINER_H__
-#define __FILTRATIONCONTAINER_H__
-
-#include "utilities/consistencylist.h"
-#include "cycle.h"
-
-/**
- * FiltrationContainer class. Serves as a parent of Filtration that
- * describes the container functionality. Used by FiltrationSimplex
- * to get Cycle representation.
- */
-template<class FltrSmplx>
-class FiltrationContainer: public ConsistencyList<FltrSmplx>
-{
- public:
- typedef FltrSmplx FiltrationSimplex;
- typedef ConsistencyList<FiltrationSimplex> ConsistencyList;
-
- /// \name Cycles and Trails
- /// @{
- /// Index is and therfore acts like an iterator. The name is preserved for historical reasons.
- typedef typename ConsistencyList::iterator Index;
- /// const_Index is a const_iterator
- typedef typename ConsistencyList::const_iterator const_Index;
- /// @}
-
- /// \name Cycles and Trails
- /// @{
- typedef typename ConsistencyList::GreaterThanComparison CyclesComparator;
- typedef typename ConsistencyList::LessThanComparison TrailsComparator;
- typedef typename ConsistencyList::ConsistencyComparison ConsistencyComparator;
- typedef ::Cycle<Index, CyclesComparator, ConsistencyComparator> Cycle;
- typedef ::Cycle<Index, TrailsComparator, ConsistencyComparator> Trail;
- /// @}
-
- template<class U>
- struct rebind { typedef FiltrationContainer<U> other; };
-};
-
-#endif // __FILTRATIONCONTAINER_H__
--- a/include/topology/filtrationsimplex.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2006
- */
-
-#ifndef __FILTRATIONSIMPLEX_H__
-#define __FILTRATIONSIMPLEX_H__
-
-#include "utilities/sys.h"
-#include "utilities/debug.h"
-
-#include "filtrationcontainer.h"
-#include "vineyard.h"
-#include "utilities/types.h"
-
-#include <list>
-
-#if 0
-#include <boost/serialization/access.hpp>
-#include <boost/serialization/nvp.hpp>
-#include <boost/serialization/list.hpp>
-#endif
-
-/**
- * Evaluator is a base class for the structures that are able to return a value
- * given a simplex.
- */
-template<class Smplx>
-class Evaluator
-{
- public:
- typedef Smplx Simplex;
-
- virtual RealType time() const { return 0; }
- virtual RealType value(const Simplex& s) const { return 0; }
-
- virtual ~Evaluator() {}
-};
-
-/**
- * FiltrationSimplex stores information needed for the RU-decomposition:
- * cycle (column of R), trail (row of U), and pair.
- */
-template<class Smplx>
-class FiltrationSimplex: public Smplx
-{
- public:
- typedef Smplx Simplex;
- typedef FiltrationSimplex<Simplex> Self;
- typedef FiltrationContainer<Self> Container;
- typedef Simplex Parent;
-
- typedef Vine<Simplex> Vine;
- typedef typename Container::Cycle Cycle;
- typedef typename Container::Trail Trail;
- typedef typename Container::Index Index;
-
- typedef Evaluator<Simplex> Evaluator;
-
- FiltrationSimplex(const Simplex& s):
- Simplex(s), vine_(0) {}
-
-
- /// \name Core functionality
- /// @{
- void set_pair(Index pair) { pair_ = pair; }
- bool sign() const { return cycles_column.empty(); }
- bool is_paired() const { return pair() != pair()->pair(); }
- void set_vine(Vine* v) { vine_ = v; }
- using Parent::dimension;
- /// @}
-
-
- /// \name Accessors
- /// @{
- Cycle& cycle() { return cycles_column; }
- Trail& trail() { return trails_row; }
- const Cycle& cycle() const { return cycles_column; }
- const Trail& trail() const { return trails_row; }
- Index pair() const { return pair_; }
- Vine* vine() const { return vine_; }
- /// @}
-
- private:
- Cycle cycles_column;
- Trail trails_row;
- Index pair_;
- Vine* vine_;
-};
-
-#endif // __FILTRATIONSIMPLEX_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/image-zigzag-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,106 @@
+#ifndef __IMAGE_ZIGZAG_PERSISTENCE_H__
+#define __IMAGE_ZIGZAG_PERSISTENCE_H__
+
+#include "zigzag-persistence.h"
+#include <limits>
+
+struct SimplexSubcomplexData
+{
+ SimplexSubcomplexData(bool sc = false):
+ subcomplex(sc) {}
+
+ bool subcomplex;
+};
+
+template<class BirthID_ = Empty<> >
+class ImageZigzagPersistence: public ZigzagPersistence<BirthID_, SimplexSubcomplexData>
+{
+ public:
+ typedef BirthID_ BirthID;
+ typedef ZigzagPersistence<BirthID, SimplexSubcomplexData> Parent;
+
+ typedef typename Parent::IndexDeathPair IndexDeathPair;
+ typedef typename Parent::Death Death;
+
+ typedef typename Parent::ZIndex ZIndex;
+ typedef typename Parent::BIndex BIndex;
+ typedef typename Parent::SimplexIndex SimplexIndex;
+ typedef typename Parent::ZColumn ZColumn;
+ typedef typename Parent::BColumn BColumn;
+ typedef typename Parent::BRow BRow;
+ typedef typename Parent::CRow CRow;
+ typedef typename Parent::ZNode ZNode;
+ typedef typename Parent::BNode BNode;
+ typedef typename Parent::SimplexNode SimplexNode;
+
+
+ ImageZigzagPersistence():
+ im_last(Parent::z_list.end()), cok_begin(Parent::z_list.end()),
+ im_order_begin(std::numeric_limits<int>::min()/2),
+ cok_order_begin(std::numeric_limits<int>::max()/2)
+ {}
+
+ IndexDeathPair add(ZColumn bdry,
+ bool subcomplex,
+ const BirthID& birth = BirthID()) { ImageZZVisitor zzv(subcomplex); return Parent::add(bdry, birth, zzv); }
+
+ Death remove(SimplexIndex s, const BirthID& birth = BirthID()) { ImageZZVisitor zzv(s->subcomplex); return Parent::remove(s, birth, zzv); }
+
+
+ ZIndex image_begin() { return Parent::z_list.begin(); }
+ ZIndex image_end() { return cok_begin; }
+ BIndex boundary_end() { return Parent::b_list.end(); }
+
+
+ // Class: ImageZZVisitor
+ // Contains all the tweaks to the normal zigzag algorithm to make it compute image zigzag
+ class ImageZZVisitor: public Parent::ZigzagVisitor
+ {
+ public:
+ ImageZZVisitor(bool sc = false):
+ subcomplex(sc), birth_in_image(false) {}
+
+ // Sets the subcomplex property of the new simplex
+ SimplexIndex new_simplex(Parent& zz);
+
+ // Decides where to put the new column (image or cokernel)
+ ZIndex new_z_in_add(Parent& zz, const ZColumn& z, const BRow& u);
+
+ // Checks if there is a boundary entirely in the subcomplex, and sets birth_in_image accordingly
+ BIndex select_j_in_remove(Parent& zz, const CRow& c_row);
+
+ ZIndex new_z_in_remove(Parent& zz);
+
+ // Updates im_last and cok_begin if necessary
+ void erasing_z(Parent& zz, ZIndex j);
+
+ // Determines if there is a death in the image
+ Death death(Parent& zz, ZIndex dying_z);
+
+ private:
+ ZIndex append_in_image(Parent& zz);
+ ZIndex append_in_cokernel(Parent& zz);
+ ZIndex prepend_in_image(Parent& zz);
+ ZIndex prepend_in_cokernel(Parent& zz);
+ bool in_subcomplex(const ZColumn& z);
+
+ bool subcomplex, birth_in_image;
+ };
+
+ const int im_order_begin;
+ const int cok_order_begin;
+
+ private:
+ using Parent::make_remover;
+ using Parent::make_appender;
+ using Parent::make_adder;
+
+ private:
+ ZIndex im_last; // index of the last image cycle
+ ZIndex cok_begin; // index of the first cokernel cycle
+};
+
+
+#include "image-zigzag-persistence.hpp"
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/image-zigzag-persistence.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,213 @@
+#ifdef LOGGING
+static rlog::RLogChannel* rlImageZigzag = DEF_CHANNEL("topology/persistence/zigzag/image", rlog::Log_Debug);
+#endif // LOGGING
+
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::SimplexIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+new_simplex(Parent& zz)
+{
+ SimplexIndex si = Parent::ZigzagVisitor::new_simplex(zz);
+ si->subcomplex = subcomplex;
+ rLog(rlImageZigzag, "New simplex %d (inL=%d)", si->order, si->subcomplex);
+ return si;
+}
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::ZIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+new_z_in_add(Parent& zz, const ZColumn& z, const BRow& u)
+{
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+
+ // Check if z is entirely in the subcomplex
+ if (in_subcomplex(z))
+ return append_in_image(zz);
+ else
+ {
+ append_in_cokernel(zz);
+
+ // Simplex we are adding is in the subcomplex
+ if (subcomplex)
+ {
+ rLog(rlImageZigzag, "Modifying boundaries");
+
+ SimplexIndex s = boost::prior(izz.s_list.end());
+ AssertMsg(s->subcomplex, "The new simplex must be in the subcomplex");
+ rLog(rlImageZigzag, " s=%d", s->order);
+ rLog(rlImageZigzag, " u=[%s]", u.tostring(izz.out).c_str());
+
+ BIndex max = u.front();
+ for (typename BRow::const_iterator cur = u.begin(); cur != u.end(); ++cur)
+ if ((*cur)->b_column.back()->order > max->b_column.back()->order)
+ max = *cur;
+
+ // Replace B[max] with sum of b_columns from u
+ BColumn sum;
+ std::for_each(u.begin(), u.end(), izz.make_adder(&BNode::b_column, sum));
+#if ZIGZAG_CONSISTENCY
+ AssertMsg(in_subcomplex(s->boundary), "Boundary of s must be in the subcomplex");
+#endif
+ std::for_each(max->b_column.begin(), max->b_column.end(), izz.make_remover(&ZNode::b_row, max));
+ rLog(rlImageZigzag, "max->b_column=[%s]", max->b_column.tostring(izz.out).c_str());
+ rLog(rlImageZigzag, " sum=[%s]", sum.tostring(izz.out).c_str());
+ AssertMsg(sum.back() == max->b_column.back(), "Must be replacing by a column with the same low");
+ max->b_column.swap(sum);
+ std::for_each(max->b_column.begin(), max->b_column.end(), izz.make_appender(&ZNode::b_row, max));
+ // NB: low doesn't need to be adjusted (see AssertMsg above)
+
+ // Wipe out C[max], and replace it with s
+ std::for_each(max->c_column.begin(), max->c_column.end(), izz.make_remover(&SimplexNode::c_row, max));
+ max->c_column.clear();
+ max->c_column.append(s, izz.cmp);
+ AssertMsg(s->c_row.empty(), "s was just added, so it cannot appear in any bounding chain");
+ s->c_row.append(max, izz.cmp);
+ }
+
+ return boost::prior(izz.z_list.end());
+ }
+}
+
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::BIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+select_j_in_remove(Parent& zz, const CRow& c_row)
+{
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+ for (typename CRow::const_iterator cur = c_row.begin(); cur != c_row.end(); ++cur)
+ if ((*cur)->b_column.back()->order <= izz.im_last->order)
+ {
+ birth_in_image = true;
+ return *cur;
+ }
+
+ return Parent::ZigzagVisitor::select_j_in_remove(zz, c_row);
+}
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::ZIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+new_z_in_remove(Parent& zz)
+{
+ if (birth_in_image)
+ return prepend_in_image(zz);
+ else
+ return prepend_in_cokernel(zz);
+}
+
+template<class BID>
+void
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+erasing_z(Parent& zz, ZIndex j)
+{
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+ if (j == izz.im_last) --(izz.im_last);
+ else if (j == izz.cok_begin) ++(izz.cok_begin);
+}
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::Death
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+death(Parent& zz, ZIndex dying_z)
+{
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+ if (izz.im_last == izz.z_list.end() || dying_z->order > izz.im_last->order)
+ return Death();
+ else
+ return Death(dying_z->birth);
+}
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::ZIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+append_in_image(Parent& zz)
+{
+ rLog(rlImageZigzag, "Appending in image");
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+
+ // if no cycles in the image
+ if (izz.im_last == izz.z_list.end())
+ {
+ izz.z_list.push_front(ZNode(izz.im_order_begin, izz.b_list.end()));
+ return (izz.im_last = izz.z_list.begin());
+ } else
+ {
+ izz.z_list.insert(boost::next(izz.im_last), ZNode(izz.im_last->order + 1, izz.b_list.end()));
+ return ++(izz.im_last);
+ }
+}
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::ZIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+append_in_cokernel(Parent& zz)
+{
+ rLog(rlImageZigzag, "Appending in cokernel");
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+
+ // if no cycles in the cokernel
+ if (izz.cok_begin == izz.z_list.end())
+ {
+ izz.z_list.push_back(ZNode(izz.cok_order_begin, izz.b_list.end()));
+ izz.cok_begin = boost::prior(izz.z_list.end());
+ } else
+ {
+ izz.z_list.push_back(ZNode(boost::prior(izz.z_list.end())->order + 1, izz.b_list.end()));
+ }
+
+ return boost::prior(izz.z_list.end());
+}
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::ZIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+prepend_in_image(Parent& zz)
+{
+ rLog(rlImageZigzag, "Prepending in image");
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+
+ // if no cycles in the image
+ if (izz.im_last == izz.z_list.end())
+ {
+ izz.z_list.push_front(ZNode(izz.im_order_begin, izz.b_list.end()));
+ return (izz.im_last = izz.z_list.begin());
+ } else
+ {
+ izz.z_list.push_front(ZNode(izz.z_list.begin()->order - 1, izz.b_list.end()));
+ return izz.z_list.begin();
+ }
+}
+
+template<class BID>
+typename ImageZigzagPersistence<BID>::ZIndex
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+prepend_in_cokernel(Parent& zz)
+{
+ rLog(rlImageZigzag, "Prepending in cokernel");
+ ImageZigzagPersistence& izz = static_cast<ImageZigzagPersistence&>(zz);
+
+ // if no cycles in the cokernel
+ if (izz.cok_begin == izz.z_list.end())
+ {
+ izz.z_list.push_back(ZNode(izz.cok_order_begin, izz.b_list.end()));
+ izz.cok_begin = boost::prior(izz.z_list.end());
+ } else
+ {
+ izz.z_list.insert(izz.cok_begin, ZNode(izz.cok_begin->order - 1, izz.b_list.end()));
+ --(izz.cok_begin);
+ }
+ return izz.cok_begin;
+}
+
+template<class BID>
+bool
+ImageZigzagPersistence<BID>::ImageZZVisitor::
+in_subcomplex(const ZColumn& z)
+{
+ for (typename ZColumn::const_iterator cur = z.begin(); cur != z.end(); ++cur)
+ if (!((*cur)->subcomplex))
+ return false;
+ return true;
+}
--- a/include/topology/lowerstarfiltration.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/lowerstarfiltration.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,114 +1,69 @@
/*
* Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
+ * Department of Computer Science, Duke University, 2005 -- 2008
*/
#ifndef __LOWERSTARFILTRATION_H__
#define __LOWERSTARFILTRATION_H__
-#include "utilities/sys.h"
-#include "utilities/debug.h"
-
-#include "filtration.h"
-#include "simplex.h"
-#include "utilities/consistencylist.h"
-#include <boost/utility.hpp>
-#include <list>
-#include "utilities/types.h"
-
#include <boost/serialization/access.hpp>
#include <boost/serialization/vector.hpp>
-#include <boost/serialization/map.hpp>
-#include <boost/serialization/base_object.hpp>
#include <boost/serialization/nvp.hpp>
+/**
+ * Struct: MaxVertexComparison
+ *
+ * Functor that determines which simplex has a higher vertex with respect to VertexComparison_, breaking ties by dimension
+ */
+template<class Simplex_, class VertexComparison_>
+struct MaxVertexComparison
+{
+ typedef VertexComparison_ VertexComparison;
+ typedef Simplex_ Simplex;
+ typedef typename Simplex::Vertex Vertex;
+
+ MaxVertexComparison(const VertexComparison& vcmp):
+ vcmp_(vcmp) {}
+
+ bool operator()(const Simplex& s1, const Simplex& s2) const
+ {
+ const Vertex& max1 = *std::max_element(s1.vertices().begin(), s1.vertices().end(), vcmp_);
+ const Vertex& max2 = *std::max_element(s2.vertices().begin(), s2.vertices().end(), vcmp_);
+
+ bool less = vcmp_(max1, max2),
+ more = vcmp_(max2, max1);
+
+ if (!less && !more) // equal
+ return s1.dimension() < s2.dimension();
+
+ return less;
+ }
+
+ VertexComparison vcmp_;
+};
+
-template<class VI,
- class Smplx = SimplexWithAttachment<VI>,
- class FltrSmplx = FiltrationSimplex<Smplx>,
- class Vnrd = Vineyard<FltrSmplx> >
-class LowerStarFiltration: public Filtration<Smplx, FltrSmplx, Vnrd>
+/**
+ * Map from i-th vertex to its index in the filtration.
+ */
+template<class Index_, class Filtration_>
+class VertexSimplexMap
{
- public:
- // Treat VertexIndex as an iterator
- typedef VI VertexIndex;
- typedef Smplx Simplex;
- typedef Filtration<Simplex> Parent;
- typedef typename Parent::Vineyard Vineyard;
-
- typedef typename Parent::Index Index;
- typedef typename Parent::const_Index const_Index;
- typedef typename Parent::Cycle Cycle;
- typedef typename Parent::Trail Trail;
- typedef typename Simplex::Cycle SimplexBoundaryCycle;
-
- struct VertexDescriptor;
- typedef ConsistencyList<VertexDescriptor> VertexOrder;
- typedef typename VertexOrder::iterator VertexOrderIndex;
- typedef typename VertexOrder::const_iterator const_VertexOrderIndex;
- typedef typename VertexOrder::LessThanComparison VertexOrderComparison;
- struct SimplexAttachmentComparison;
-
- public:
- template<class VertexCmp>
- LowerStarFiltration(VertexIndex begin, VertexIndex end, const VertexCmp& cmp, Vineyard* vineyard);
+ public:
+ typedef Index_ Index;
+ typedef Filtration_ Filtration;
+ typedef std::vector<Index> VertexVector;
+
+ VertexSimplexMap(Index begin, Index end, const Filtration& f)
+ {
+ for (Index cur = begin; cur != end; ++cur)
+ if (f.simplex(cur).dimension() == 0)
+ vertices_.push_back(cur);
+ }
- using Parent::size;
- using Parent::begin;
- using Parent::end;
- VertexIndex num_vertices() const { return vertex_order.size(); }
- const VertexOrderComparison&
- get_vertex_cmp() const { return vertex_cmp; }
-
- Index append(const Simplex& s);
- bool transpose_vertices(const VertexOrderIndex& voi);
-
- protected:
- /// Hint function: if unsure, should return true
- virtual bool neighbors(VertexIndex v1, VertexIndex v2) const { return true; }
-
- private:
- bool transpose(Index i);
- void assert_pairing(Index i);
-
- private:
- VertexOrder vertex_order;
- VertexOrderComparison vertex_cmp;
-
- /* Serialization */
- protected:
- LowerStarFiltration() {}
-
- private:
- friend class boost::serialization::access;
-
- template<class Archive>
- void save(Archive& ar, version_type ) const { ar << BOOST_SERIALIZATION_BASE_OBJECT_NVP(Parent); }
-
- template<class Archive>
- void load(Archive& ar, version_type );
-
- BOOST_SERIALIZATION_SPLIT_MEMBER()
+ private:
+ VertexVector vertices_;
};
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-struct LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::VertexDescriptor
-{
- VertexDescriptor(VertexIndex vi, Index si):
- vertex_index(vi), simplex_index(si)
- {}
-
- VertexIndex vertex_index;
- Index simplex_index;
-};
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-struct LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::SimplexAttachmentComparison
-{
- bool operator()(const Simplex& first, const Simplex& second) const;
- VertexOrderComparison vertex_cmp;
-};
-
-#include "lowerstarfiltration.hpp"
#endif // __LOWERSTARFILTRATION_H__
--- a/include/topology/lowerstarfiltration.hpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-/* Implementations */
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-template<class VertexCmp>
-LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::
-LowerStarFiltration(VertexIndex begin, VertexIndex end, const VertexCmp& cmp, Vineyard* vineyard):
- Parent(vineyard)
-{
- // Record VertexIndexes in a temporary list
- typedef std::list<VertexIndex> VertexIndexList;
- VertexIndexList tmp_list;
- while (begin != end)
- tmp_list.push_back(begin++);
-
- // Sort the temporary list
- tmp_list.sort(cmp);
-
- // Record vertex order
- for(typename VertexIndexList::const_iterator cur = tmp_list.begin(); cur != tmp_list.end(); ++cur)
- (*cur)->set_order(vertex_order.push_back(VertexDescriptor(*cur, Parent::append(Simplex(*cur)))));
-}
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-typename LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::Index
-LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::
-append(const Simplex& s)
-{
- AssertMsg(s.dimension() != 0, "All vertices must have been inserted in the constructor");
-
- // Find the highest vertex
- typename Simplex::VertexContainer::const_iterator cur = s.vertices().begin(), max = cur++;
- for (; cur != s.vertices().end(); ++cur)
- if (!vertex_cmp((*cur)->get_order(), (*max)->get_order()))
- max = cur;
-
- Index ms = (*max)->get_order()->simplex_index; Index prior;
- do { prior = ms++; } while (ms->dimension() <= s.dimension() && ms != Parent::end() && ms->get_attachment() == *max);
-
- Index i = Parent::insert(prior, s);
- i->set_attachment(*max);
-
- return i;
-}
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-bool
-LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::SimplexAttachmentComparison::
-operator()(const Simplex& first, const Simplex& second) const
-{
- int cmp = vertex_cmp.compare(first.get_attachment()->get_order(), second.get_attachment()->get_order());
- if (cmp == 0)
- return first.dimension() < second.dimension();
- else
- return cmp == -1;
-}
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-bool
-LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::
-transpose_vertices(const VertexOrderIndex& order)
-{
- Count("VertexTransposition");
-
-#if COUNTERS
- if ((counters.lookup("VertexTransposition") % 1000000) == 0)
- {
- Dout(dc::lsfiltration, "Vertex transpositions: " << counters.lookup("VertexTransposition"));
- Dout(dc::lsfiltration, "Simplex transpositions: " << counters.lookup("SimplexTransposition"));
- Dout(dc::lsfiltration, "Attachment changed: " << counters.lookup("ChangedAttachment"));
- Dout(dc::lsfiltration, "Regular disconnected: " << counters.lookup("RegularDisconnected"));
- Dout(dc::lsfiltration, "Pairing Changed: " << counters.lookup("ChangedPairing"));
- Dout(dc::lsfiltration, "------------------------");
- }
-#endif // COUNTERS
-
- Dout(dc::lsfiltration, "Transposing vertices (" << order->vertex_index << ", "
- << boost::next(order)->vertex_index << ")");
-
- Index i = order->simplex_index;
- Index i_prev = boost::prior(i);
- Index i_next = boost::next(order)->simplex_index;
- Index i_next_prev = boost::prior(i_next); // transpositions are done in terms of the first index in the pair
- Index j = boost::next(i_next);
-
- const VertexIndex& v_i = order->vertex_index;
- const VertexIndex& v_i_next = boost::next(order)->vertex_index;
- bool nbghrs = neighbors(v_i, v_i_next);
-
- bool result = false;
-
- // First, move the vertex --- this can be sped up if we devise special "vertex transpose" operation
- while (i_next_prev != i_prev)
- {
- result |= transpose(i_next_prev);
- i_next_prev = boost::prior(i_next);
- }
- Dout(dc::lsfiltration, "Done moving the vertex");
-
- // Second, move the simplices attached to it
- Dout(dc::lsfiltration, "Moving attached simplices");
- while (j != Parent::end() && j->get_attachment() == v_i_next)
- {
- Dout(dc::lsfiltration, " Considering " << *j);
- if (nbghrs && j->contains(v_i)) // short circuit
- {
- Count("ChangedAttachment");
- Dout(dc::lsfiltration, " Attachment changed for " << *j);
- j->set_attachment(v_i);
- ++j;
- continue;
- }
-
- Index j_prev = j; ++j;
- while ((--j_prev)->get_attachment() != v_i_next) // i.e., until we have reached v_i_next
- // (and the simplices that follow it) again
- {
- Dout(dc::lsfiltration, " Moving: " << *j_prev << ", " << *boost::next(j_prev));
- AssertMsg(j_prev->get_attachment() == v_i, "Simplex preceding the one being moved must be attached to v_i");
- result |= transpose(j_prev);
- --j_prev;
- }
- }
- Dout(dc::lsfiltration, "Done moving attached simplices");
- vertex_order.swap(order, boost::next(order));
-
- return result;
-}
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-bool
-LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::
-transpose(Index i)
-{
- Index j = boost::next(i);
-
- Dout(dc::lsfiltration, " Transposing (" << *i << ", " << *(i->pair()) << ") and ("
- << *j << ", " << *(j->pair()) << ")");
-
- assert_pairing(i);
- assert_pairing(j);
-
- bool res = Parent::transpose(i, false);
- Dout(dc::lsfiltration, " " << *j << ": " << *(j->pair()) << ", " << *i << ": " << *(i->pair()));
-
- assert_pairing(j);
- assert_pairing(i);
-
- return res;
-}
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-void
-LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::
-assert_pairing(Index i)
-{
-#ifndef NDEBUG
- AssertMsg(i != Parent::end(), "Cannot assert pairing of end()");
- if (!i->sign())
- {
- if (i->pair() != i->cycle().top(Parent::get_cycles_cmp()))
- {
- Dout(dc::lsfiltration, "i (negative): " << *i);
- Dout(dc::lsfiltration, "pair(i): " << *(i->pair()));
- Dout(dc::lsfiltration, "i->cycle().top(): " << *(i->cycle().top(Parent::get_cycles_cmp())));
- DoutFatal(dc::fatal, "Pairing not matching the matrix at " << *i);
- }
- } else
- {
- if (i->pair() != i)
- {
- if (i->pair()->cycle().top(Parent::get_cycles_cmp()) != i)
- {
- Dout(dc::lsfiltration, "i (positive): " << *i);
- Dout(dc::lsfiltration, "pair(i): " << *(i->pair()));
- Dout(dc::lsfiltration, "pair(i)->cycle(): " << i->pair()->cycle());
- Dout(dc::lsfiltration, "pair->cycle().top(): " << *(i->pair()->cycle().top(Parent::get_cycles_cmp())));
- DoutFatal(dc::fatal, "Pairing not matching the matrix at " << *(i->pair()));
- }
- }
- }
-#endif
-}
-
-
-template<class VI, class Smplx, class FltrSmplx, class Vnrd>
-template<class Archive>
-void
-LowerStarFiltration<VI,Smplx,FltrSmplx,Vnrd>::
-load(Archive& ar, version_type )
-{
-/*
- ar >> BOOST_SERIALIZATION_BASE_OBJECT_NVP(Parent);
-
- // Count the number of vertices
- VertexIndex num_vertices = 0;
- for (Index cur = begin(); cur != end(); ++cur)
- if (dimension(cur) == 0)
- num_vertices++;
-
- // Second pass to record vertex_order
- vertex_order.resize(num_vertices);
- inverse_vertex_order.resize(num_vertices);
- num_vertices = 0;
- for (Index cur = begin(); cur != end(); ++cur)
- {
- if (dimension(cur) == 0)
- {
- vertex_order[num_vertices].index = cur;
- vertex_order[num_vertices].vertex_index = *(cur->get_vertices().begin());
- inverse_vertex_order[vertex_order[num_vertices].vertex_index] = num_vertices;
- ++num_vertices;
- }
- }
-*/
-}
-
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/lsvineyard.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,255 @@
+/**
+ * Author: Dmitriy Morozov
+ * Department of Computer Science, Duke University, 2005 -- 2010
+ */
+
+#ifndef __LSVINEYARD_H__
+#define __LSVINEYARD_H__
+
+#include <iostream>
+
+#include "topology/simplex.h"
+#include "topology/dynamic-persistence.h"
+#include "topology/lowerstarfiltration.h"
+#include "topology/vineyard.h"
+
+#include <utilities/indirect.h>
+
+#include <geometry/simulator.h>
+#include <geometry/kinetic-sort.h>
+#include <geometry/linear-kernel.h>
+
+#include <boost/tuple/tuple.hpp>
+namespace b = boost;
+
+
+template<class Vertex_, class VertexEvaluator_, class Simplex_ = Simplex<Vertex_>, class Filtration_ = Filtration<Simplex_> >
+class LSVineyard
+{
+ public:
+ typedef LSVineyard Self;
+
+ typedef Vertex_ Vertex;
+ typedef VertexEvaluator_ VertexEvaluator;
+ typedef typename VertexEvaluator::result_type VertexValue;
+
+ typedef Simplex_ Simplex;
+ typedef Filtration_ LSFiltration;
+ typedef typename LSFiltration::Index LSFIndex;
+
+ typedef LinearKernel<VertexValue> KineticKernel;
+ typedef Simulator<KineticKernel> KineticSimulator;
+ class KineticVertexType;
+ class KineticVertexComparison;
+ class TrajectoryExtractor;
+ typedef typename OrderContainer<KineticVertexType>::Container
+ VertexContainer;
+ typedef typename VertexContainer::iterator VertexIndex;
+
+ struct AttachmentData: public VineData
+ {
+ void set_attachment(VertexIndex v) { attachment = v; }
+ VertexIndex attachment;
+ };
+ typedef DynamicPersistenceTrails<AttachmentData> Persistence;
+ typedef typename Persistence::OrderIndex Index;
+ typedef typename Persistence::iterator iterator;
+
+ typedef typename Persistence::template SimplexMap<LSFiltration>
+ PFMap;
+
+ class Evaluator;
+ class StaticEvaluator;
+ class KineticEvaluator;
+ class DimensionFromIterator;
+
+ typedef std::map<Vertex, LSFIndex> VertexLSFIndexMap;
+ typedef ThroughEvaluatorComparison<VertexEvaluator> VertexComparison;
+ class VertexAttachmentComparison;
+ typedef MaxVertexComparison<Simplex, VertexComparison> SimplexComparison;
+
+ class TranspositionVisitor;
+ friend class TranspositionVisitor;
+
+ typedef Vineyard<Index, iterator, Evaluator> Vnrd;
+
+ public:
+ template<class VertexIterator>
+ LSVineyard(VertexIterator begin, VertexIterator end,
+ LSFiltration& filtration,
+ const VertexEvaluator& veval = VertexEvaluator());
+ ~LSVineyard();
+
+ void compute_vineyard(const VertexEvaluator& veval);
+ bool transpose_vertices(VertexIndex vi);
+
+ const LSFiltration& filtration() const { return filtration_; }
+ const Vnrd& vineyard() const { return vineyard_; }
+ const Persistence& persistence() const { return persistence_; }
+ const VertexComparison& vertex_comparison() const { return vcmp_; }
+ const VertexEvaluator& vertex_evaluator() const { return veval_; }
+ const SimplexComparison& simplex_comparison() const { return scmp_; }
+
+ VertexValue vertex_value(const Vertex& v) const { return veval_(v); }
+ VertexValue simplex_value(const Simplex& s) const { return vertex_value(*std::max_element(s.vertices().begin(), s.vertices().end(), vcmp_)); }
+ const Simplex& pfmap(iterator i) const { return pfmap_[i]; }
+ const Simplex& pfmap(Index i) const { return pfmap_[i]; }
+ VertexIndex filtration_attachment(LSFIndex i) const { return (persistence().begin() + (i - filtration().begin()))->attachment; }
+
+ Index index(iterator i) const { return persistence_.index(i); }
+
+ public:
+ // For Kinetic Sort
+ void swap(VertexIndex a, KineticSimulator* simulator);
+
+ private:
+ void change_evaluator(Evaluator* eval);
+ void set_attachment(iterator i, VertexIndex vi) { persistence_.modifier()(i, boost::bind(&AttachmentData::set_attachment, bl::_1, vi)); }
+ void transpose_filtration(iterator i) { filtration_.transpose(filtration_.begin() + (i - persistence_.begin())); }
+
+ bool verify_pairing() const;
+
+ typedef b::tuple< b::reference_wrapper<const Simplex>,
+ b::reference_wrapper<const typename Persistence::Element> >
+ SimplexPersistenceElementTuple;
+ struct AttachmentCmp;
+
+ private:
+ VertexContainer vertices_;
+
+ VertexEvaluator veval_;
+ VertexComparison vcmp_;
+ SimplexComparison scmp_;
+
+ LSFiltration& filtration_;
+ Persistence persistence_;
+ PFMap pfmap_;
+
+ Vnrd vineyard_;
+ Evaluator* evaluator_;
+ unsigned time_count_;
+
+#if 0
+ private:
+ // Serialization
+ friend class boost::serialization::access;
+
+ LSVineyard() {}
+
+ template<class Archive>
+ void serialize(Archive& ar, version_type )
+ {
+ ar & BOOST_SERIALIZATION_NVP(grid_stack_);
+ ar & BOOST_SERIALIZATION_NVP(vertices_);
+ ar & BOOST_SERIALIZATION_NVP(filtration_);
+ };
+#endif
+};
+
+//BOOST_CLASS_EXPORT(LSVineyard)
+
+template<class V, class VE, class S, class C>
+std::ostream&
+operator<<(std::ostream& out, const typename LSVineyard<V,VE,S,C>::VertexIndex& vi)
+{ return out << vi->vertex(); }
+
+template<class V, class VE, class S, class C>
+std::ostream&
+operator<<(std::ostream& out, const typename LSVineyard<V,VE,S,C>::KineticVertexType& v)
+{ return out << v.vertex(); }
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::KineticVertexType
+{
+ public:
+ KineticVertexType(const Vertex& v):
+ vertex_(v) {}
+
+ Vertex vertex() const { return vertex_; }
+ void set_vertex(Vertex v) { vertex_ = v; }
+
+ LSFIndex simplex_index() const { return simplex_index_; }
+ void set_simplex_index(LSFIndex i) { simplex_index_ = i; }
+
+ private:
+ Vertex vertex_;
+ LSFIndex simplex_index_;
+};
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::TrajectoryExtractor: public std::unary_function<VertexIndex, typename KineticSimulator::Function>
+{
+ public:
+ typedef typename KineticSimulator::Function Function;
+
+ TrajectoryExtractor(const VertexEvaluator& veval0,
+ const VertexEvaluator& veval1):
+ veval0_(veval0), veval1_(veval1) {}
+
+
+ Function operator()(VertexIndex i) const { VertexValue v0 = veval0_(i->vertex()), v1 = veval1_(i->vertex()); return Function(v0, v1 - v0); }
+
+ private:
+ const VertexEvaluator& veval0_, veval1_;
+};
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::KineticVertexComparison: public std::binary_function<const KineticVertexType&, const KineticVertexType&, bool>
+{
+ public:
+ KineticVertexComparison(const VertexComparison& vcmp):
+ vcmp_(vcmp) {}
+
+ bool operator()(const KineticVertexType& v1, const KineticVertexType& v2) const
+ { return vcmp_(v1.vertex(), v2.vertex()); }
+
+ private:
+ VertexComparison vcmp_;
+};
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::TranspositionVisitor: public Persistence::TranspositionVisitor
+{
+ public:
+ typedef typename Persistence::TranspositionVisitor Parent;
+ typedef typename LSVineyard<V,VE,S,C>::iterator iterator;
+ typedef typename LSVineyard<V,VE,S,C>::Index Index;
+
+ TranspositionVisitor(LSVineyard& v): lsvineyard_(v) {}
+
+ void transpose(iterator i) { lsvineyard_.transpose_filtration(i); }
+ void switched(iterator i, SwitchType type) { lsvineyard_.vineyard_.switched(index(i), index(boost::next(i))); }
+
+ private:
+ Index index(iterator i) { return lsvineyard_.index(i); }
+
+ LSVineyard& lsvineyard_;
+};
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::Evaluator: public std::unary_function<Index, RealType>
+{
+ public:
+ virtual ~Evaluator() {}
+ virtual RealType time() const =0;
+ virtual RealType operator()(Index i) const =0;
+ virtual Dimension dimension(Index i) const =0;
+ virtual RealType operator()(iterator i) const { return operator()(&*i); }
+ virtual Dimension dimension(iterator i) const { return dimension(&*i); }
+};
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::DimensionFromIterator: std::unary_function<iterator, Dimension>
+{
+ public:
+ DimensionFromIterator(const PFMap& pfmap): pfmap_(pfmap) {}
+
+ Dimension operator()(iterator i) const { return pfmap_[i].dimension(); }
+
+ private:
+ const PFMap& pfmap_;
+};
+
+#include "lsvineyard.hpp"
+
+#endif // __LSVINEYARD_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/lsvineyard.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,358 @@
+#include <utilities/log.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+namespace bl = boost::lambda;
+
+#include <boost/iterator/zip_iterator.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/foreach.hpp>
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlLSVineyard = DEF_CHANNEL("lsvineyard/info", rlog::Log_Debug);
+static rlog::RLogChannel* rlLSVineyardDebug = DEF_CHANNEL("lsvineyard/debug", rlog::Log_Debug);
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cVertexTransposition = GetCounter("lsfiltration/transposition"); // counts number of vertex transpositions
+static Counter* cAttachment = GetCounter("lsfiltration/attachment"); // counts the number of attachment changes
+#endif
+
+
+template<class V, class VE, class S, class F>
+template<class VertexIterator>
+LSVineyard<V,VE,S,F>::
+LSVineyard(VertexIterator begin, VertexIterator end,
+ LSFiltration& fltr,
+ const VertexEvaluator& veval):
+ filtration_(fltr),
+ vertices_(begin, end),
+ persistence_(filtration_),
+ veval_(veval), vcmp_(veval_), scmp_(vcmp_),
+ pfmap_(persistence_.make_simplex_map(filtration_)),
+ time_count_(0)
+{
+ vertices_.sort(KineticVertexComparison(vcmp_)); // sort vertices w.r.t. vcmp_
+#if LOGGING
+ rLog(rlLSVineyardDebug, "Vertex order:");
+ for (typename VertexContainer::iterator cur = vertices_.begin(); cur != vertices_.end(); ++cur)
+ rLog(rlLSVineyardDebug, " %d", cur->vertex());
+#endif
+
+ // Record Vertex -> LSFIndex map
+ VertexLSFIndexMap vimap;
+ for (LSFIndex i = filtration().begin(); i != filtration().end(); ++i)
+ {
+ const Simplex& s = *i;
+ rLog(rlLSVineyardDebug, "Simplex: %s", tostring(*i).c_str());
+ if (s.dimension() == 0)
+ vimap[s.vertices().front()] = i;
+ }
+
+ // Assign vertex attachments and simplex_index
+ OffsetMap<LSFIndex, iterator> fpmap(filtration().begin(), persistence().begin());
+ for (typename VertexContainer::iterator vi = vertices_.begin(); vi != vertices_.end(); ++vi)
+ {
+ LSFIndex i = vimap[vi->vertex()];
+ const Simplex& s = *i;
+ AssertMsg(s.vertices().front() == vi->vertex(), "In constructor, simplices and vertices must match.");
+ vertices_.modify(vi, b::bind(&KineticVertexType::set_simplex_index, bl::_1, i)); // vi->set_simplex_index(i)
+ set_attachment(fpmap[i], vi);
+ rLog(rlLSVineyardDebug, "%s attached to %d", tostring(*i).c_str(), vi->vertex());
+ }
+
+ // Assign attachments for all the simplices
+ VertexAttachmentComparison vacmp(vimap, *this);
+ for (LSFIndex i = filtration().begin(); i != filtration().end(); ++i)
+ set_attachment(fpmap[i], fpmap[vimap[*std::max_element(i->vertices().begin(), i->vertices().end(), vacmp)]]->attachment);
+
+ // Order filtration_ and persistence_ based on attachment
+ rLog(rlLSVineyardDebug, "Ordering the simplices");
+
+ std::vector<SimplexPersistenceElementTuple> fporder
+ (b::make_zip_iterator(b::make_tuple(filtration().begin(), persistence().begin())),
+ b::make_zip_iterator(b::make_tuple(filtration().end(), persistence().end())));
+ std::sort(fporder.begin(), fporder.end(), AttachmentCmp());
+
+ // Rearrage filtration
+ std::vector< b::reference_wrapper<const Simplex> > sv;
+ BOOST_FOREACH(const SimplexPersistenceElementTuple& t, fporder) sv.push_back(b::get<0>(t));
+ filtration_.rearrange (sv.begin());
+
+ // Rearrange persistence
+ std::vector< b::reference_wrapper<const typename Persistence::Element> > pev;
+ BOOST_FOREACH(const SimplexPersistenceElementTuple& t, fporder) pev.push_back(b::get<1>(t));
+ persistence_.rearrange(pev.begin());
+
+#if LOGGING
+ rLog(rlLSVineyardDebug, "Simplices:");
+ for(iterator i = persistence().begin(); i != persistence().end(); ++i)
+ rLog(rlLSVineyardDebug, " %s attached to %d", tostring(pfmap(i)).c_str(), i->attachment->vertex());
+#endif
+
+ // Pair simplices
+ rLog(rlLSVineyardDebug, "Initializing LSVineyard");
+ persistence_.pair_simplices();
+ rLog(rlLSVineyardDebug, "Simplices paired");
+
+ evaluator_ = new StaticEvaluator(*this, time_count_);
+ vineyard_.set_evaluator(evaluator_);
+ vineyard_.start_vines(persistence_.begin(), persistence_.end());
+}
+
+template<class V, class VE, class S, class F>
+LSVineyard<V,VE,S,F>::
+~LSVineyard()
+{
+ delete evaluator_;
+}
+
+template<class V, class VE, class S, class F_>
+void
+LSVineyard<V,VE,S,F_>::
+compute_vineyard(const VertexEvaluator& veval)
+{
+ typedef KineticSort<VertexIndex, TrajectoryExtractor, KineticSimulator> KineticSortDS;
+
+ // Setup the (linear) trajectories
+ rLog(rlLSVineyard, "Setting up trajectories");
+ KineticSimulator simulator;
+ TrajectoryExtractor traj(veval_, veval);
+
+ KineticSortDS sort(vertices_.begin(), vertices_.end(),
+ boost::bind(&LSVineyard::swap, this, bl::_1, bl::_2),
+ &simulator, traj);
+
+ // Process all the events (compute the vineyard in the process)
+ change_evaluator(new KineticEvaluator(*this, simulator, time_count_, traj));
+ while (!simulator.reached_infinity() && simulator.next_event_time() < 1)
+ {
+ rLog(rlLSVineyardDebug, "Next event time: %f", simulator.next_event_time());
+ simulator.process();
+ rLog(rlLSVineyardDebug, "Processed event");
+ }
+ rLog(rlLSVineyard, "Processed %d events", simulator.event_count());
+ // AssertMsg(sort.audit(&simulator), "Sort audit should succeed");
+
+ veval_ = veval;
+ change_evaluator(new StaticEvaluator(*this, ++time_count_));
+ vineyard_.record_diagram(persistence().begin(), persistence().end());
+}
+
+template<class V, class VE, class S, class F>
+void
+LSVineyard<V,VE,S,F>::
+swap(VertexIndex a, KineticSimulator* simulator)
+{
+ VertexIndex b = boost::next(a);
+ rLog(rlLSVineyardDebug, "Entered swap");
+ rLog(rlLSVineyardDebug, "Vertices: %d %d compare %d", a->vertex(), b->vertex(), vcmp_(a->vertex(), b->vertex()));
+ AssertMsg(!vcmp_(b->vertex(), a->vertex()), "In swap(a,b), a must precede b"); // true since we are using linear iterpolation
+ AssertMsg(a < b, "In swap(a,b), a must precede b");
+ transpose_vertices(a);
+ AssertMsg(b < a, "In swap(a,b), b must precede a after the transposition");
+}
+
+template<class V, class VE, class S, class F>
+void
+LSVineyard<V,VE,S,F>::
+change_evaluator(Evaluator* eval)
+{
+ AssertMsg(evaluator_ != 0, "change_evaluator() assumes that existing evaluator is not null");
+
+ delete evaluator_;
+ evaluator_ = eval;
+ vineyard_.set_evaluator(evaluator_);
+}
+
+template<class V, class VE, class S, class F>
+bool
+LSVineyard<V,VE,S,F>::
+transpose_vertices(VertexIndex vi)
+{
+ Count(cVertexTransposition);
+ rLog(rlLSVineyard, "Transposing vertices (%d:%d, %d:%d)", vi->vertex(), (vi - vertices_.begin()),
+ b::next(vi)->vertex(), (b::next(vi) - vertices_.begin()));
+
+ DimensionFromIterator dim(pfmap_);
+ TranspositionVisitor visitor(*this);
+
+ OffsetMap<LSFIndex, iterator> fpmap(filtration().begin(), persistence().begin());
+ iterator i = fpmap[vi->simplex_index()];
+ iterator i_prev = b::prior(i);
+ iterator i_next = fpmap[b::next(vi)->simplex_index()];
+ iterator i_next_prev = b::prior(i_next); // transpositions are done in terms of the first index in the pair
+ iterator j = b::next(i_next);
+
+ VertexIndex vi_next = b::next(vi);
+ const Vertex& v = vi->vertex();
+
+ bool result = false; // has a switch in pairing occurred
+
+ // First move the vertex --- this can be sped up if we devise special "vertex transpose" operation
+ rLog(rlLSVineyardDebug, "Starting to move the vertex");
+ while (i_next_prev != i_prev)
+ {
+ rLog(rlLSVineyardDebug, " Transposing %s %s", tostring(pfmap(i_next_prev)).c_str(),
+ tostring(pfmap(b::next(i_next_prev))).c_str());
+ result |= persistence_.transpose(i_next_prev, dim, visitor);
+ AssertMsg((i_next_prev <= persistence().iterator_to(i_next_prev->pair)) == i_next_prev->sign(), "Pairing must respect order");
+ AssertMsg((i_next <= persistence().iterator_to(i_next->pair)) == i_next->sign(), "Pairing must respect order");
+ i_next_prev = b::prior(i_next);
+ }
+ rLog(rlLSVineyardDebug, "Done moving the vertex");
+
+ // Second, move the simplices attached to it
+ rLog(rlLSVineyardDebug, "Moving attached simplices");
+ // rLog(rlLSVineyardDebug, " Considering %s", tostring(pfmap(j)).c_str());
+ // rLog(rlLSVineyardDebug, " attachment %d", j->attachment->vertex());
+ while (j != persistence_.end() && j->attachment == vi_next)
+ {
+ rLog(rlLSVineyardDebug, " Considering %s", tostring(pfmap(j)).c_str());
+ if (pfmap(j).contains(v)) // j becomes attached to v and does not move
+ {
+ Count(cAttachment);
+ rLog(rlLSVineyardDebug, " Attachment changed for %s to %d", tostring(pfmap(j)).c_str(), vi->vertex());
+ set_attachment(j, vi);
+ AssertMsg(fpmap[vi->simplex_index()] < j, "The simplex must be attached to a preceding vertex");
+ ++j;
+ continue;
+ }
+
+ iterator j_prev = j; ++j;
+ while ((--j_prev)->attachment != vi_next) // i.e., until we have reached vi_next (and the simplices that follow it) again
+ {
+ rLog(rlLSVineyardDebug, " Moving: %s, %s",
+ tostring(pfmap(j_prev)).c_str(),
+ tostring(pfmap(b::next(j_prev))).c_str());
+ AssertMsg(j_prev->attachment == vi, "Simplex preceding the one being moved must be attached to v");
+ result |= persistence_.transpose(j_prev, dim, visitor);
+ AssertMsg((j_prev <= persistence().iterator_to(j_prev->pair)) == j_prev->sign(), "Pairing must respect order");
+ --j_prev;
+ }
+ }
+ rLog(rlLSVineyard, "Done moving attached simplices");
+ vertices_.relocate(vi, vi_next); // swap vi and vi_next
+
+#if LSVINEYARD_CONSISTENCY
+ AssertMsg(verify_pairing(), "Pairing must be correct after vertex transposition");
+#endif
+
+ return result;
+}
+
+template<class V, class VE, class S, class F>
+bool
+LSVineyard<V,VE,S,F>::
+verify_pairing() const
+{
+ rLog(rlLSVineyardDebug, "Verifying pairing");
+ StaticPersistence<> p(filtration());
+ p.pair_simplices(false);
+ iterator i = persistence().begin();
+ StaticPersistence<>::iterator ip = p.begin();
+ StaticPersistence<>::SimplexMap<LSFiltration> m = p.make_simplex_map(filtration());
+
+ while (ip != p.end())
+ {
+ if (&pfmap(i) != &m[ip])
+ {
+ rError("DP: %s %s", tostring(pfmap(i)).c_str(), tostring(pfmap(i->pair)).c_str());
+ rError("SP: %s %s", tostring(m[ip]).c_str(), tostring(m[ip->pair]).c_str());
+ rError("The order must match");
+ return false;
+ }
+ if (&pfmap(i->pair) != &m[ip->pair])
+ {
+ rError("DP: %s %s", tostring(pfmap(i)).c_str(), tostring(pfmap(i->pair)).c_str());
+ rError("SP: %s %s", tostring(m[ip]).c_str(), tostring(m[ip->pair]).c_str());
+ rError("The pairing must match");
+ return false;
+ }
+ ++i; ++ip;
+ }
+
+ return true;
+}
+
+
+/* Evaluators */
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::StaticEvaluator: public Evaluator
+{
+ public:
+ StaticEvaluator(const LSVineyard& v, RealType time):
+ time_(time), vineyard_(v) {}
+
+ virtual RealType time() const { return time_; }
+ virtual RealType operator()(Index i) const { return vineyard_.simplex_value(vineyard_.pfmap(i)); }
+ virtual Dimension dimension(Index i) const { return vineyard_.pfmap(i).dimension(); }
+
+ private:
+ RealType time_;
+ const LSVineyard& vineyard_;
+};
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::KineticEvaluator: public Evaluator
+{
+ public:
+ typedef typename KineticSimulator::Time Time;
+
+ KineticEvaluator(const LSVineyard& v, const KineticSimulator& sp, RealType time_offset, const TrajectoryExtractor& traj):
+ vineyard_(v), sp_(sp),
+ time_offset_(time_offset), traj_(traj) {}
+
+ virtual RealType time() const { return time_offset_ + get_time(); }
+ virtual RealType operator()(Index i) const
+ {
+ rLog(rlLSVineyard, "%s (attached to %d): %s(%f) = %f", tostring(vineyard_.pfmap(i)).c_str(),
+ i->attachment->vertex(),
+ tostring(traj_(i->attachment)).c_str(),
+ get_time(),
+ traj_(i->attachment)(get_time()));
+ return traj_(i->attachment)(get_time());
+ }
+ virtual Dimension dimension(Index i) const { return vineyard_.pfmap(i).dimension(); }
+
+ private:
+ Time get_time() const { return sp_.current_time(); }
+
+ const LSVineyard& vineyard_;
+ const KineticSimulator& sp_;
+ const TrajectoryExtractor& traj_;
+ RealType time_offset_;
+};
+
+
+template<class V, class VE, class S, class C>
+class LSVineyard<V,VE,S,C>::VertexAttachmentComparison:
+ public std::binary_function<Vertex, Vertex, bool>
+{
+ public:
+ VertexAttachmentComparison(const VertexLSFIndexMap& vimap,
+ const LSVineyard& vnrd):
+ vimap_(vimap), vnrd_(vnrd) {}
+ bool operator()(Vertex v1, Vertex v2) const
+ { return vnrd_.filtration_attachment(vimap_.find(v1)->second) < vnrd_.filtration_attachment(vimap_.find(v2)->second); }
+
+ private:
+ const VertexLSFIndexMap& vimap_;
+ const LSVineyard& vnrd_;
+};
+
+
+template<class V, class VE, class S, class C>
+struct LSVineyard<V,VE,S,C>::AttachmentCmp:
+ public std::binary_function<const SimplexPersistenceElementTuple&, const SimplexPersistenceElementTuple&, bool>
+{
+ bool operator()(const SimplexPersistenceElementTuple& t1, const SimplexPersistenceElementTuple& t2) const
+ {
+ if (b::get<1>(t1).get().attachment == b::get<1>(t2).get().attachment)
+ return b::get<0>(t1).get().dimension() < b::get<0>(t2).get().dimension();
+ else
+ return b::get<1>(t1).get().attachment < b::get<1>(t2).get().attachment;
+ }
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/order.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,113 @@
+#ifndef __ORDER_H__
+#define __ORDER_H__
+
+#include "utilities/types.h"
+#include "utilities/indirect.h"
+#include "utilities/property-maps.h"
+
+#include <vector>
+#include <list>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/random_access_index.hpp>
+namespace bmi = boost::multi_index;
+
+//#include <iostream>
+#include <sstream>
+#include <string>
+
+
+/* Tags */
+// TODO: pollutes global namespace; find a place to localize
+struct order {};
+struct consistency {};
+
+template<class Container>
+class OffsetOutputMap
+{
+ public:
+ typedef const typename Container::value_type* const_element_pointer;
+ typedef typename Container::iterator iterator;
+
+ OffsetOutputMap(const Container& order):
+ om(order.begin(),0), order_(order) {}
+
+ // returns a string with (i - bg_)
+ std::string operator()(iterator i) const
+ {
+ std::stringstream s;
+ s << om[i];
+ return s.str();
+ }
+
+ std::string operator()(const_element_pointer p) const
+ {
+ return (*this)(order_.iterator_to(*p));
+ }
+
+ private:
+ OffsetMap<typename Container::iterator, unsigned> om;
+ const Container& order_;
+};
+
+template<class Element_ = Empty<> >
+struct OrderContainer
+{
+ typedef Element_ Element;
+ typedef boost::multi_index_container<Element,
+ bmi::indexed_by<
+ bmi::random_access<bmi::tag<order> > /* order index */
+ >
+ > Container;
+ typedef typename Container::template index<order>::type OrderedContainer;
+
+ typedef OffsetOutputMap<Container> OutputMap;
+
+ template<class U> struct rebind
+ { typedef OrderContainer<U> other; };
+};
+
+
+template<class Container_, class Comparison_>
+struct ElementComparison: public std::binary_function<const typename Container_::value_type*,
+ const typename Container_::value_type*,
+ bool>
+{
+ typedef Container_ Container;
+ typedef Comparison_ Comparison;
+ typedef typename Container::value_type Element;
+
+ ElementComparison(const Container& container,
+ const Comparison& cmp = Comparison()):
+ container_(container), cmp_(cmp) {}
+
+ bool operator()(const Element* a, const Element* b) const { return cmp_(container_.iterator_to(*a), container_.iterator_to(*b)); }
+
+ const Container& container_;
+ const Comparison& cmp_;
+};
+
+
+
+template<class Element_ = Empty<> >
+struct OrderConsistencyContainer
+{
+ typedef Element_ Element;
+ typedef boost::multi_index_container<Element,
+ bmi::indexed_by<
+ bmi::random_access<bmi::tag<order> >, /* current index */
+ bmi::random_access<bmi::tag<consistency> > /* original index */
+ >
+ > Container;
+
+ typedef typename Container::template index<order>::type OrderedContainer;
+ typedef typename Container::template index<consistency>::type ConsistentContainer;
+
+ typedef OffsetOutputMap<Container> OutputMap;
+
+ template<class U> struct rebind
+ { typedef OrderConsistencyContainer<U> other; };
+};
+
+
+#endif // __ORDER_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/persistence-diagram.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,178 @@
+#ifndef __PERSISTENCE_DIAGRAM_H__
+#define __PERSISTENCE_DIAGRAM_H__
+
+#include <utilities/types.h>
+
+#include <vector>
+#include <iostream>
+#include <cmath>
+
+#include <boost/compressed_pair.hpp>
+#include <boost/optional.hpp>
+#include <boost/serialization/access.hpp>
+
+/**
+ * Class: PDPoint
+ *
+ * Stores birth-death pair plus any additional information provided by `Data` template parameter.
+ */
+template<class Data_ = Empty<> >
+class PDPoint
+{
+ public:
+ typedef Data_ Data;
+
+ PDPoint(const PDPoint& other):
+ point_(other.point_) {}
+ PDPoint(RealType x = 0, RealType y = 0, const Data& data = Data());
+
+ RealType x() const { return point_.first().first; }
+ RealType y() const { return point_.first().second; }
+ const Data& data() const { return point_.second(); }
+ Data& data() { return point_.second(); }
+
+ std::ostream& operator<<(std::ostream& out) const { return (out << x() << " " << y()); } // << " " << data()); }
+
+ struct Visitor
+ {
+ template<class Iterator>
+ void point(Iterator i, PDPoint& p) const {}
+ };
+
+ private:
+ RealType& x() { return point_.first().first; }
+ RealType& y() { return point_.first().second; }
+
+ private:
+ boost::compressed_pair<std::pair<RealType, RealType>, Data> point_;
+
+ private:
+ /* Serialization */
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, version_type );
+};
+
+template<class Data>
+std::ostream& operator<<(std::ostream& out, const PDPoint<Data>& point)
+{ return (point.operator<<(out)); }
+
+template<class Point, class Iterator, class Evaluator, class Visitor>
+boost::optional<Point>
+make_point(Iterator i, const Evaluator& evaluator, const Visitor& visitor);
+
+template<class Point, class Iterator, class Evaluator>
+boost::optional<Point>
+make_point(Iterator i, const Evaluator& evaluator)
+{ return make_point<Point>(i, evaluator, Point::Visitor()); }
+
+
+/**
+ * Class: PersistenceDiagram
+ *
+ * Stores birth-death pairs, i.e. points in the extended plane. Each point can also store
+ * additional information described by `Data_` template parameter.
+ */
+template<class Data_ = Empty<> >
+class PersistenceDiagram
+{
+ public:
+ typedef Data_ Data;
+ typedef PDPoint<Data> Point;
+ typedef std::vector<Point> PointVector;
+ typedef typename PointVector::const_iterator const_iterator;
+
+ PersistenceDiagram() {}
+
+ PersistenceDiagram( Dimension dimension ):
+ dimension_( dimension ) {}
+
+ template<class OtherData>
+ PersistenceDiagram(const PersistenceDiagram<OtherData>& other);
+
+ template<class Iterator, class Evaluator>
+ PersistenceDiagram(Iterator bg, Iterator end,
+ const Evaluator& eval = Evaluator());
+
+ template<class Iterator, class Evaluator, class Visitor>
+ PersistenceDiagram(Iterator bg, Iterator end,
+ const Evaluator& eval = Evaluator(),
+ const Visitor& visitor = Visitor());
+
+ template<class Iterator, class Evaluator, class Visitor>
+ void init(Iterator bg, Iterator end,
+ const Evaluator& eval = Evaluator(),
+ const Visitor& visitor = Visitor());
+
+ const_iterator begin() const { return points_.begin(); }
+ const_iterator end() const { return points_.end(); }
+ size_t size() const { return points_.size(); }
+
+ void push_back(const Point& point) { points_.push_back(point); }
+
+ std::ostream& operator<<(std::ostream& out) const;
+
+ Dimension dimension() const { return dimension_; }
+
+ private:
+ PointVector points_;
+ Dimension dimension_;
+
+ private:
+ /* Serialization */
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, version_type );
+};
+
+template<class Data>
+std::ostream& operator<<(std::ostream& out, const PersistenceDiagram<Data>& pd)
+{ return (pd.operator<<(out)); }
+
+// Function: init_diagram_vector()
+template<class Diagrams, class Iterator, class Evaluator, class DimensionExtractor>
+void init_diagrams(Diagrams& diagrams,
+ Iterator bg, Iterator end,
+ const Evaluator& evaluator = Evaluator(),
+ const DimensionExtractor& dimension = DimensionExtractor());
+
+template<class Diagrams, class Iterator, class Evaluator, class DimensionExtractor, class Visitor>
+void init_diagrams(Diagrams& diagrams,
+ Iterator bg, Iterator end,
+ const Evaluator& evaluator = Evaluator(),
+ const DimensionExtractor& dimension = DimensionExtractor(),
+ const Visitor& visitor = Visitor());
+
+// Class: Linfty
+// Functor that computes L infinity norm between two points
+template<class Point1, class Point2>
+struct Linfty
+{
+ RealType operator()(const Point1& p1, const Point2& p2) const { return std::max(std::abs(p1.x() - p2.x()),
+ std::abs(p1.y() - p2.y())); }
+
+ template<class Point>
+ RealType diagonal(const Point& p) const { return std::abs(p.y() - p.x())/2; }
+};
+
+// Function: bottleneck_distance(dgm1, dgm2)
+// Computes bottleneck distance between the two diagrams.
+template<class Diagram1,
+ class Diagram2,
+ class Norm>
+RealType bottleneck_distance(const Diagram1& dgm1, const Diagram2& dgm2, const Norm& norm = Norm());
+
+template<class Diagram1,
+ class Diagram2>
+RealType bottleneck_distance(const Diagram1& dgm1, const Diagram2& dgm2)
+{ return bottleneck_distance(dgm1, dgm2, Linfty<typename Diagram1::Point, typename Diagram2::Point>()); }
+
+template<class Diagram>
+RealType wasserstein_distance(const Diagram& dgm1, const Diagram& dgm2, unsigned p);
+
+
+#include "persistence-diagram.hpp"
+
+#endif // __PERSISTENCE_DIAGRAM_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/persistence-diagram.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,326 @@
+#include <boost/serialization/vector.hpp>
+#include <boost/serialization/nvp.hpp>
+
+#include "utilities/munkres/munkres.h"
+
+using boost::serialization::make_nvp;
+
+template<class D>
+PDPoint<D>::
+PDPoint(RealType x, RealType y, const Data& data)
+{
+ point_.first().first = x;
+ point_.first().second = y;
+ point_.second() = data;
+}
+
+
+template<class D>
+template<class OtherData>
+PersistenceDiagram<D>::
+PersistenceDiagram(const PersistenceDiagram<OtherData>& other)
+{
+ points_.reserve(other.size());
+ for (typename PersistenceDiagram<OtherData>::PointVector::const_iterator cur = points_.begin();
+ cur != points_.end(); ++cur)
+ push_back(Point(cur->x(), cur->y()));
+}
+
+template<class D>
+template<class Iterator, class Evaluator>
+PersistenceDiagram<D>::
+PersistenceDiagram(Iterator bg, Iterator end, const Evaluator& eval)
+{
+ init(bg, end, eval, Point::Visitor());
+}
+
+template<class D>
+template<class Iterator, class Evaluator, class Visitor>
+PersistenceDiagram<D>::
+PersistenceDiagram(Iterator bg, Iterator end, const Evaluator& eval, const Visitor& visitor)
+{
+ init(bg, end, eval, visitor);
+}
+
+template<class D>
+template<class Iterator, class Evaluator, class Visitor>
+void
+PersistenceDiagram<D>::
+init(Iterator bg, Iterator end, const Evaluator& evaluator, const Visitor& visitor)
+{
+ for (Iterator cur = bg; cur != end; ++cur)
+ if (cur->sign())
+ {
+ boost::optional<Point> p = make_point(cur, evaluator, visitor);
+ if (p) push_back(*p);
+ }
+}
+
+template<class Point, class Iterator, class Evaluator, class Visitor>
+boost::optional<Point>
+make_point(Iterator i, const Evaluator& evaluator, const Visitor& visitor)
+{
+ RealType x = evaluator(&*i);
+ RealType y = Infinity;
+ if (&*(i->pair) != &*i)
+ y = evaluator(&*(i->pair));
+
+ Point p(x,y);
+ visitor.point(i, p);
+
+ if (x == y) return boost::optional<Point>();
+
+ return p;
+}
+
+template<class Diagrams, class Iterator, class Evaluator, class DimensionExtractor>
+void init_diagrams(Diagrams& diagrams,
+ Iterator bg, Iterator end,
+ const Evaluator& evaluator,
+ const DimensionExtractor& dimension)
+{
+ // FIXME: this is specialized for Diagrams that is std::map
+ typedef typename Diagrams::mapped_type PDiagram;
+
+ init_diagrams(diagrams, bg, end, evaluator, dimension, typename PDiagram::Point::Visitor());
+}
+
+template<class Diagrams, class Iterator, class Evaluator, class DimensionExtractor, class Visitor>
+void init_diagrams(Diagrams& diagrams,
+ Iterator bg, Iterator end,
+ const Evaluator& evaluator,
+ const DimensionExtractor& dimension,
+ const Visitor& visitor)
+{
+ // FIXME: this is specialized for Diagrams that is std::map
+ typedef typename Diagrams::mapped_type PDiagram;
+
+ for (Iterator cur = bg; cur != end; ++cur)
+ if (cur->sign())
+ {
+ boost::optional<typename PDiagram::Point> p = make_point<typename PDiagram::Point>(cur, evaluator, visitor);
+ if (p)
+ diagrams[dimension(&*cur)].push_back(*p);
+ }
+}
+
+template<class D>
+std::ostream&
+PersistenceDiagram<D>::
+operator<<(std::ostream& out) const
+{
+ for (const_iterator cur = begin(); cur != end(); ++cur)
+ out << *cur << std::endl;
+ return out;
+}
+
+template<class D>
+template<class Archive>
+void
+PDPoint<D>::
+serialize(Archive& ar, version_type )
+{
+ ar & make_nvp("x", x());
+ ar & make_nvp("y", y());
+ ar & make_nvp("data", data());
+}
+
+template<class D>
+template<class Archive>
+void
+PersistenceDiagram<D>::
+serialize(Archive& ar, version_type )
+{
+ ar & make_nvp("points", points_);
+}
+
+
+/**
+ * Some structures to compute bottleneck distance between two persistence diagrams (in bottleneck_distance() function below)
+ * by setting up bipartite graphs, and finding maximum cardinality matchings in them using Boost Graph Library.
+ */
+#include <boost/iterator/counting_iterator.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/max_cardinality_matching.hpp>
+
+struct Edge: public std::pair<unsigned, unsigned>
+{
+ typedef std::pair<unsigned, unsigned> Parent;
+
+ Edge(unsigned v1, unsigned v2, RealType d):
+ Parent(v1, v2), distance(d) {}
+
+ bool operator<(const Edge& other) const { return distance < other.distance; }
+
+ RealType distance;
+};
+typedef std::vector<Edge> EdgeVector;
+typedef EdgeVector::const_iterator EV_const_iterator;
+
+struct CardinaliyComparison
+{
+ typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph;
+ typedef std::vector<boost::graph_traits<Graph>::vertex_descriptor> MatchingVector;
+
+ CardinaliyComparison(unsigned size, EV_const_iterator begin):
+ max_size(size), bg(begin), last(bg), g(2*max_size), mates(2*max_size)
+ { boost::add_edge(bg->first, bg->second, g); }
+
+ bool operator()(EV_const_iterator i1, EV_const_iterator i2)
+ {
+ //std::cout << "Max size: " << max_size << std::endl;
+ //std::cout << "Comparing: (" << i1->first << ", " << i1->second << ") and "
+ // << "(" << i2->first << ", " << i2->second << ")" << std::endl;
+
+ // FIXME: the matching is being recomputed from scratch every time, this should be fixed
+ if (i2 > last)
+ do
+ {
+ ++last;
+ boost::add_edge(last->first, last->second, g);
+ } while (last != i2);
+ else
+ do
+ {
+ boost::remove_edge(last->first, last->second, g);
+ } while (--last != i2);
+
+ edmonds_maximum_cardinality_matching(g, &mates[0]);
+ //std::cout << "Found matching of size: " << matching_size(g, &mates[0]) << std::endl;
+ return matching_size(g, &mates[0]) == max_size;
+ }
+
+ unsigned max_size;
+ EV_const_iterator bg;
+ EV_const_iterator last;
+ Graph g;
+ MatchingVector mates;
+};
+
+// Bottleneck distance
+template<class Diagram1, class Diagram2, class Norm>
+RealType bottleneck_distance(const Diagram1& dgm1, const Diagram2& dgm2, const Norm& norm)
+{
+ typedef typename Diagram1::const_iterator Citer1;
+ typedef typename Diagram2::const_iterator Citer2;
+
+ const unsigned max_size = dgm1.size() + dgm2.size();
+
+ // Compute all the edges and sort them by distance
+ EdgeVector edges;
+
+ // Connect all diagonal points to each other
+ for (unsigned i = dgm1.size(); i < max_size; ++i)
+ for (unsigned j = max_size + dgm2.size(); j < 2*max_size; ++j)
+ edges.push_back(Edge(i, j, 0));
+
+ // Edges between real points
+ unsigned i = 0;
+ for (Citer1 cur1 = dgm1.begin(); cur1 != dgm1.end(); ++cur1)
+ {
+ unsigned j = max_size;
+ for (Citer2 cur2 = dgm2.begin(); cur2 != dgm2.end(); ++cur2)
+ edges.push_back(Edge(i,j++, norm(*cur1, *cur2)));
+
+ ++i;
+ }
+
+ // Edges between real points and their corresponding diagonal points
+ i = 0;
+ for (Citer1 cur1 = dgm1.begin(); cur1 != dgm1.end(); ++cur1, ++i)
+ edges.push_back(Edge(i, max_size + dgm2.size() + i, norm.diagonal(*cur1)));
+ i = max_size;
+ for (Citer2 cur2 = dgm2.begin(); cur2 != dgm2.end(); ++cur2, ++i)
+ edges.push_back(Edge(dgm1.size() + (i - max_size), i, norm.diagonal(*cur2)));
+
+
+ std::sort(edges.begin(), edges.end());
+ //for (i = 0; i < edges.size(); ++i)
+ // std::cout << "Edge: " << edges[i].first << " " << edges[i].second << " " << edges[i].distance << std::endl;
+
+ // Perform cardinality based binary search
+ typedef boost::counting_iterator<EV_const_iterator> EV_counting_const_iterator;
+ EV_counting_const_iterator bdistance = std::upper_bound(EV_counting_const_iterator(edges.begin()),
+ EV_counting_const_iterator(edges.end()),
+ edges.begin(),
+ CardinaliyComparison(max_size, edges.begin()));
+
+ return (*bdistance)->distance;
+}
+
+// Wasserstein distance
+template<class Diagram>
+RealType
+wasserstein_distance(const Diagram& dgm1, const Diagram& dgm2, unsigned p)
+{
+ typedef RealType Distance;
+ typedef typename Diagram::Point Point;
+ typedef Linfty<Point, Point> Norm;
+
+ unsigned size = dgm1.size() + dgm2.size();
+ Norm norm;
+
+ // Setup the matrix
+ Matrix<Distance> m(size,size);
+ for (unsigned i = 0; i < dgm1.size(); ++i)
+ for (unsigned j = 0; j < dgm2.size(); ++j)
+ {
+ const Point& p1 = *(dgm1.begin() + i);
+ const Point& p2 = *(dgm2.begin() + j);
+ m(i,j) = pow(norm(p1, p2), p);
+ m(j + dgm1.size(), i + dgm2.size()) = 0;
+ }
+
+ for (unsigned i = 0; i < dgm1.size(); ++i)
+ for (unsigned j = dgm2.size(); j < size; ++j)
+ {
+ const Point& p1 = *(dgm1.begin() + i);
+ m(i,j) = pow(norm.diagonal(p1), p);
+ }
+
+ for (unsigned j = 0; j < dgm2.size(); ++j)
+ for (unsigned i = dgm1.size(); i < size; ++i)
+ {
+ const Point& p2 = *(dgm2.begin() + j);
+ m(i,j) = pow(norm.diagonal(p2), p);
+ }
+
+ // Compute weighted matching
+ Munkres munkres;
+ munkres.solve(m);
+
+ // Assume everything is assigned (i.e., that we have a perfect matching)
+ Distance sum = 0;
+ for (unsigned i = 0; i < size; i++)
+ for (unsigned j = 0; j < size; j++)
+ if (m(i,j) == 0)
+ {
+ //std::cout << i << ": " << j << '\n';
+ //sum += m[i][j];
+ if (i >= dgm1.size())
+ {
+ if (j >= dgm2.size())
+ sum += 0;
+ else
+ {
+ const Point& p2 = *(dgm2.begin() + j);
+ sum += pow(norm.diagonal(p2), p);
+ }
+ } else
+ {
+ if (j >= dgm2.size())
+ {
+ const Point& p1 = *(dgm1.begin() + i);
+ sum += pow(norm.diagonal(p1), p);
+ } else
+ {
+ const Point& p1 = *(dgm1.begin() + i);
+ const Point& p2 = *(dgm2.begin() + j);
+ sum += pow(norm(p1, p2), p);
+ }
+ }
+ break;
+ }
+
+ return sum;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/rips.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,176 @@
+#ifndef __RIPS_H__
+#define __RIPS_H__
+
+#include <vector>
+#include <string>
+#include "simplex.h"
+#include <boost/iterator/counting_iterator.hpp>
+
+
+/**
+ * Rips class
+ *
+ * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm,
+ * and provides simple wrappers for various functions.
+ *
+ * Distances_ is expected to define types IndexType and DistanceType as well as
+ * provide operator()(...) which given two IndexTypes should return
+ * the distance between them. There should be methods begin() and end()
+ * for iterating over IndexTypes as well as a method size().
+ */
+template<class Distances_, class Simplex_ = Simplex<typename Distances_::IndexType> >
+class Rips
+{
+ public:
+ typedef Distances_ Distances;
+ typedef typename Distances::IndexType IndexType;
+ typedef typename Distances::DistanceType DistanceType;
+
+ typedef Simplex_ Simplex;
+ typedef typename Simplex::Vertex Vertex; // should be the same as IndexType
+ typedef typename Simplex::VertexContainer VertexContainer;
+
+ class Evaluator;
+ class Comparison;
+ class ComparePair;
+
+ public:
+ Rips(const Distances& distances):
+ distances_(distances) {}
+
+ // Calls functor f on each simplex in the k-skeleton of the Rips complex
+ template<class Functor, class Iterator>
+ void generate(Dimension k, DistanceType max, const Functor& f,
+ Iterator candidates_begin, Iterator candidates_end) const;
+
+ // Calls functor f on all the simplices of the Rips complex that contain the given vertex v
+ template<class Functor, class Iterator>
+ void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f,
+ Iterator candidates_begin, Iterator candidates_end) const;
+
+ // Calls functor f on all the simplices of the Rips complex that contain the given edge [u,v]
+ template<class Functor, class Iterator>
+ void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f,
+ Iterator candidates_begin, Iterator candidates_end) const;
+
+ // Calls functor f on all the simplices of the Rips complex that contain the given Simplex s
+ // (unlike the previous methods it does not call the functor on the Simplex s itself)
+ template<class Functor, class Iterator>
+ void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f,
+ Iterator candidates_begin, Iterator candidates_end) const;
+
+
+ /* No Iterator argument means Iterator = IndexType and the range is [distances().begin(), distances().end()) */
+ template<class Functor>
+ void generate(Dimension k, DistanceType max, const Functor& f) const
+ { generate(k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); }
+
+ template<class Functor>
+ void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f) const
+ { vertex_cofaces(v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); }
+
+ template<class Functor>
+ void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f) const
+ { edge_cofaces(u, v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); }
+
+ template<class Functor>
+ void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f) const
+ { cofaces(s, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); }
+
+
+ const Distances& distances() const { return distances_; }
+ DistanceType max_distance() const;
+
+ DistanceType distance(const Simplex& s1, const Simplex& s2) const;
+
+
+ protected:
+ class WithinDistance;
+
+ template<class Functor, class NeighborTest>
+ void bron_kerbosch(VertexContainer& current,
+ const VertexContainer& candidates,
+ typename VertexContainer::const_iterator excluded,
+ Dimension max_dim,
+ const NeighborTest& neighbor,
+ const Functor& functor,
+ bool check_initial = true) const;
+
+ protected:
+ const Distances& distances_;
+};
+
+
+template<class Distances_, class Simplex_>
+class Rips<Distances_, Simplex_>::WithinDistance: public std::binary_function<Vertex, Vertex, bool>
+{
+ public:
+ WithinDistance(const Distances_& distances,
+ DistanceType max):
+ distances_(distances), max_(max) {}
+
+ bool operator()(Vertex u, Vertex v) const { return distances_(u, v) <= max_; }
+
+ protected:
+ const Distances& distances_;
+ DistanceType max_;
+};
+
+template<class Distances_, class Simplex_>
+class Rips<Distances_, Simplex_>::Evaluator: public std::unary_function<const Simplex&, DistanceType>
+{
+ public:
+ typedef Simplex_ Simplex;
+
+ Evaluator(const Distances& distances):
+ distances_(distances) {}
+
+ DistanceType operator()(const Simplex& s) const;
+
+ protected:
+ const Distances& distances_;
+};
+
+template<class Distances_, class Simplex_>
+class Rips<Distances_, Simplex_>::Comparison: public std::binary_function<const Simplex&, const Simplex&, bool>
+{
+ public:
+ typedef Simplex_ Simplex;
+
+ Comparison(const Distances& distances):
+ eval_(distances) {}
+
+ bool operator()(const Simplex& s1, const Simplex& s2) const
+ {
+ DistanceType e1 = eval_(s1),
+ e2 = eval_(s2);
+ if (e1 == e2)
+ return s1.dimension() < s2.dimension();
+
+ return e1 < e2;
+ }
+
+ protected:
+ Evaluator eval_;
+};
+
+template<class Distances_, class Simplex_>
+struct Rips<Distances_, Simplex_>::ComparePair:
+ public std::binary_function<const std::pair<IndexType, IndexType>&,
+ const std::pair<IndexType, IndexType>&,
+ bool>
+{
+ ComparePair(const Distances& distances):
+ distances_(distances) {}
+
+ bool operator()(const std::pair<IndexType, IndexType>& a,
+ const std::pair<IndexType, IndexType>& b) { return distances_(a.first, a.second) <
+ distances_(b.first, b.second); }
+
+ const Distances& distances_;
+};
+
+
+#include "rips.hpp"
+
+#endif // __RIPS_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/rips.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,197 @@
+#include <algorithm>
+#include <utility>
+#include <boost/utility.hpp>
+#include <iostream>
+#include <utilities/log.h>
+#include <utilities/counter.h>
+#include <utilities/indirect.h>
+#include <boost/iterator/counting_iterator.hpp>
+#include <functional>
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlRips = DEF_CHANNEL("rips/info", rlog::Log_Debug);
+static rlog::RLogChannel* rlRipsDebug = DEF_CHANNEL("rips/debug", rlog::Log_Debug);
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cClique = GetCounter("rips/clique");
+#endif // COUNTERS
+
+template<class D, class S>
+template<class Functor, class Iterator>
+void
+Rips<D,S>::
+generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
+{
+ rLog(rlRipsDebug, "Entered generate with %d indices", distances().size());
+
+ WithinDistance neighbor(distances(), max);
+
+ // current = empty
+ // candidates = everything
+ VertexContainer current;
+ VertexContainer candidates(bg, end);
+ bron_kerbosch(current, candidates, boost::prior(candidates.begin()), k, neighbor, f);
+}
+
+template<class D, class S>
+template<class Functor, class Iterator>
+void
+Rips<D,S>::
+vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
+{
+ WithinDistance neighbor(distances(), max);
+
+ // current = [v]
+ // candidates = everything - [v]
+ VertexContainer current; current.push_back(v);
+ VertexContainer candidates;
+ for (Iterator cur = bg; cur != end; ++cur)
+ if (*cur != v && neighbor(v, *cur))
+ candidates.push_back(*cur);
+ bron_kerbosch(current, candidates, boost::prior(candidates.begin()), k, neighbor, f);
+}
+
+template<class D, class S>
+template<class Functor, class Iterator>
+void
+Rips<D,S>::
+edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
+{
+ rLog(rlRipsDebug, "In edge_cofaces(%d, %d)", u, v);
+
+ WithinDistance neighbor(distances(), max);
+ AssertMsg(neighbor(u,v), "The new edge must be in the complex");
+
+ // current = [u,v]
+ // candidates = everything - [u,v]
+ VertexContainer current; current.push_back(u); current.push_back(v);
+
+ VertexContainer candidates;
+ for (Iterator cur = bg; cur != end; ++cur)
+ if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur))
+ {
+ candidates.push_back(*cur);
+ rLog(rlRipsDebug, " added candidate: %d", *cur);
+ }
+
+ bron_kerbosch(current, candidates, boost::prior(candidates.begin()), k, neighbor, f);
+}
+
+template<class D, class S>
+template<class Functor, class Iterator>
+void
+Rips<D,S>::
+cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
+{
+ rLog(rlRipsDebug, "In cofaces(%s)", tostring(s).c_str());
+
+ WithinDistance neighbor(distances(), max);
+
+ // current = s.vertices()
+ VertexContainer current(s.vertices().begin(), s.vertices().end());
+
+ // candidates = everything - s.vertices() that is a neighbor() of every vertex in the simplex
+ VertexContainer candidates;
+ typedef difference_iterator<Iterator,
+ typename VertexContainer::const_iterator,
+ std::less<Vertex> > DifferenceIterator;
+ for (DifferenceIterator cur = DifferenceIterator(bg, end, s.vertices().begin(), s.vertices().end());
+ cur != DifferenceIterator(end, end, s.vertices().end(), s.vertices().end());
+ ++cur)
+ {
+ bool nghbr = true;
+ for (typename VertexContainer::const_iterator v = s.vertices().begin(); v != s.vertices().end(); ++v)
+ if (!neighbor(*v, *cur)) { nghbr = false; break; }
+
+ if (nghbr)
+ {
+ candidates.push_back(*cur);
+ rLog(rlRipsDebug, " added candidate: %d", *cur);
+ }
+ }
+
+ bron_kerbosch(current, candidates, boost::prior(candidates.begin()), k, neighbor, f, false);
+}
+
+
+template<class D, class S>
+template<class Functor, class NeighborTest>
+void
+Rips<D,S>::
+bron_kerbosch(VertexContainer& current,
+ const VertexContainer& candidates,
+ typename VertexContainer::const_iterator excluded,
+ Dimension max_dim,
+ const NeighborTest& neighbor,
+ const Functor& functor,
+ bool check_initial) const
+{
+ rLog(rlRipsDebug, "Entered bron_kerbosch");
+
+ if (check_initial && !current.empty())
+ {
+ Simplex s(current);
+ rLog(rlRipsDebug, "Reporting simplex: %s", tostring(s).c_str());
+ functor(s);
+ }
+
+ if (current.size() == static_cast<size_t>(max_dim) + 1)
+ return;
+
+ rLog(rlRipsDebug, "Traversing %d vertices", candidates.end() - boost::next(excluded));
+ for (typename VertexContainer::const_iterator cur = boost::next(excluded); cur != candidates.end(); ++cur)
+ {
+ current.push_back(*cur);
+ rLog(rlRipsDebug, " current.size() = %d, current.back() = %d", current.size(), current.back());
+
+ VertexContainer new_candidates;
+ for (typename VertexContainer::const_iterator ccur = candidates.begin(); ccur != cur; ++ccur)
+ if (neighbor(*ccur, *cur))
+ new_candidates.push_back(*ccur);
+ size_t ex = new_candidates.size();
+ for (typename VertexContainer::const_iterator ccur = boost::next(cur); ccur != candidates.end(); ++ccur)
+ if (neighbor(*ccur, *cur))
+ new_candidates.push_back(*ccur);
+ excluded = new_candidates.begin() + (ex - 1);
+
+ bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor);
+ current.pop_back();
+ }
+}
+
+template<class Distances_, class Simplex_>
+typename Rips<Distances_, Simplex_>::DistanceType
+Rips<Distances_, Simplex_>::
+distance(const Simplex& s1, const Simplex& s2) const
+{
+ DistanceType mx = 0;
+ for (typename Simplex::VertexContainer::const_iterator a = s1.vertices().begin(); a != s1.vertices().end(); ++a)
+ for (typename Simplex::VertexContainer::const_iterator b = s2.vertices().begin(); b != s2.vertices().end(); ++b)
+ mx = std::max(mx, distances_(*a,*b));
+ return mx;
+}
+
+template<class Distances_, class Simplex_>
+typename Rips<Distances_, Simplex_>::DistanceType
+Rips<Distances_, Simplex_>::
+max_distance() const
+{
+ DistanceType mx = 0;
+ for (IndexType a = distances_.begin(); a != distances_.end(); ++a)
+ for (IndexType b = boost::next(a); b != distances_.end(); ++b)
+ mx = std::max(mx, distances_(a,b));
+ return mx;
+}
+
+template<class Distances_, class Simplex_>
+typename Rips<Distances_, Simplex_>::DistanceType
+Rips<Distances_, Simplex_>::Evaluator::
+operator()(const Simplex& s) const
+{
+ DistanceType mx = 0;
+ for (typename Simplex::VertexContainer::const_iterator a = s.vertices().begin(); a != s.vertices().end(); ++a)
+ for (typename Simplex::VertexContainer::const_iterator b = boost::next(a); b != s.vertices().end(); ++b)
+ mx = std::max(mx, distances_(*a,*b));
+ return mx;
+}
--- a/include/topology/simplex.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/simplex.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,170 +1,223 @@
/*
* Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
+ * Department of Computer Science, Duke University, 2005 -- 2008
*/
#ifndef __SIMPLEX_H__
#define __SIMPLEX_H__
-#include "utilities/sys.h"
-#include "utilities/debug.h"
-
-#include <set>
+#include <vector>
+#include <algorithm>
#include <list>
#include <iostream>
#include "utilities/types.h"
+#include <boost/compressed_pair.hpp>
+#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/serialization/access.hpp>
/**
- * SimplexWithVertices is a basic simplex class. It stores vertices of a given type,
- * and knows how to compute its own boundary. It should probably be used as a base
- * class for any explicit simplex representation.
+ * Class: Simplex
+ * Basic simplex class. It stores vertices and data of the given types in
+ * a `boost::compressed_pair` (so if data is an Empty class which is the default,
+ * it incurs no space overhead). The class knows how to compute its own <boundary()>.
+ *
+ * Parameter:
+ * V - vertex type
+ * T - data type
+ *
+ * \ingroup topology
*/
-template<class V>
-class SimplexWithVertices
+template<class V, class T = Empty<> >
+class Simplex
{
- public:
- typedef V Vertex;
- typedef SimplexWithVertices<Vertex> Self;
-
- typedef std::set<Vertex> VertexContainer;
- typedef std::list<Self> Cycle;
-
- /// \name Constructors
- /// @{
- SimplexWithVertices() {}
- SimplexWithVertices(const Self& s):
- vertices_(s.vertices_) {}
- template<class Iterator>
- SimplexWithVertices(Iterator bg, Iterator end):
- vertices_(bg, end) {}
- SimplexWithVertices(const VertexContainer& v):
- vertices_(v) {}
- SimplexWithVertices(Vertex v):
- vertices_() { vertices_.insert(v); }
- /// @}
-
- /// \name Core
- /// @{
- Cycle boundary() const;
- Dimension dimension() const { return vertices_.size()-1; }
- /// @}
-
- /// \name Vertex manipulation
- /// @{
- bool contains(const Vertex& v) const { return (vertices_.find(v) != vertices_.end()); }
- const VertexContainer& vertices() const { return vertices_; }
- void add(const Vertex& v) { vertices_.insert(v); }
- /// @}
+ public:
+ /* Typedefs: Public Types
+ *
+ * Vertex - vertex type (template parameter V)
+ * Data - data type (template parameter T)
+ * Self -
+ * Boundary - type in which the boundary is stored
+ */
+ typedef V Vertex;
+ typedef T Data;
+ typedef Simplex<Vertex, Data> Self;
+ class BoundaryIterator;
- /// \name Assignment and comparison
- /// Gives an ordering on simplices (for example, so that simplices can be used as keys for std::map)
- /// @{
- const Self& operator=(const Self& s) { vertices_ = s.vertices_; return *this; }
- bool operator==(const Self& s) const { return vertices_ == s.vertices_; }
- bool operator<(const Self& s) const { return vertices_ < s.vertices_; }
- /// @}
+ /* Typedefs: Internal representation
+ *
+ * VertexContainer - internal representation of the vertices
+ * VerticesDataPair - `compressed_pair` of VertexContainer and Data
+ */
+ typedef std::vector<Vertex> VertexContainer;
+ typedef boost::compressed_pair<VertexContainer, Data> VerticesDataPair;
+
+ /// \name Constructors
+ /// @{
+ //
+ /// Constructor: Simplex()
+ /// Default constructor
+ Simplex() {}
+ /// Constructor: Simplex(Self other)
+ /// Copy constructor
+ Simplex(const Self& other):
+ vdpair_(other.vertices(), other.data()) {}
+ /// Constructor: Simplex(Data d)
+ /// Initialize simplex with data
+ Simplex(const Data& d) { data() = d; }
+ /// Constructor: Simplex(Iterator bg, Iterator end)
+ /// Initialize simplex by copying vertices in range [bg, end)
+ template<class Iterator>
+ Simplex(Iterator bg, Iterator end, const Data& d = Data()) { join(bg, end); data() = d; }
+ /// Constructor: Simplex(VertexContainer v)
+ /// Initialize simplex by copying the given VertexContainer
+ Simplex(const VertexContainer& v, const Data& d = Data()):
+ vdpair_(v, d) { std::sort(vertices().begin(), vertices().end()); }
+ /// @}
+
+ /// \name Core
+ /// @{
+ ///
+ /// Functions: boundary_begin(), boundary_end()
+ /// Returns the iterators over the boundary of the simplex
+ BoundaryIterator boundary_begin() const;
+ BoundaryIterator boundary_end() const;
+ /// Function: dimension()
+ /// Returns the dimension of the simplex
+ Dimension dimension() const { return vertices().size() - 1; }
+ /// @}
+
+ const Data& data() const { return vdpair_.second(); }
+ Data& data() { return vdpair_.second(); }
+ const VertexContainer& vertices() const { return vdpair_.first(); }
+
+ /// \name Vertex manipulation
+ /// @{
+ bool contains(const Vertex& v) const;
+ bool contains(const Self& s) const;
+ void add(const Vertex& v);
+ template<class Iterator>
+ void join(Iterator bg, Iterator end);
+ void join(const Self& other) { join(other.vertices().begin(), other.vertices().end()); }
+ /// @}
- std::ostream& operator<<(std::ostream& out) const;
-
- private:
- VertexContainer vertices_;
+ const Self& operator=(const Self& s) { vdpair_ = s.vdpair_; return *this; }
+
+ std::ostream& operator<<(std::ostream& out) const;
+
+ /* Classes: Comparisons
+ *
+ * VertexComparison - compare simplices based on the lexicographic ordering of their <vertices()>
+ * VertexDimensionComparison - compare simplices based on the lexicographic ordering of their <vertices()> within each dimension
+ * DataComparison - compare simplices based on their <data()>
+ * DataDimensionComparison - compare simplices based on their <data()> within each <dimension()>
+ */
+ struct VertexComparison;
+ struct VertexDimensionComparison;
+ struct DataComparison;
+ struct DataDimensionComparison;
- private:
- /* Serialization */
- friend class boost::serialization::access;
-
- template<class Archive>
- void serialize(Archive& ar, version_type );
+ /* Classes: Functors
+ *
+ * DataEvaluator - return data given a simplex
+ * DimensionExtractor - return dimesnion given a simplex
+ */
+ struct DataEvaluator;
+ struct DimensionExtractor;
+
+ protected:
+ VertexContainer& vertices() { return vdpair_.first(); }
+
+ VerticesDataPair vdpair_;
+
+ protected:
+ /* Serialization */
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, version_type );
};
-/**
- * SimplexWithValue explicitly adds a RealType value to the SimplexWithVertices.
- */
-template<class Vert>
-class SimplexWithValue: public SimplexWithVertices<Vert>
+
+template<class V, class T>
+struct Simplex<V,T>::VertexComparison
{
- public:
- typedef Vert Vertex;
- typedef RealType Value;
- typedef SimplexWithValue<Vertex> Self;
- typedef SimplexWithVertices<Vertex> Parent;
+ typedef Self first_argument_type;
+ typedef Self second_argument_type;
+ typedef bool result_type;
+
+ bool operator()(const Self& a, const Self& b) const { return a.vertices() < b.vertices(); }
+};
+
+template<class V, class T>
+struct Simplex<V,T>::VertexDimensionComparison
+{
+ typedef Self first_argument_type;
+ typedef Self second_argument_type;
+ typedef bool result_type;
- typedef typename Parent::VertexContainer VertexContainer;
-
- /// \name Constructors
- /// @{
- SimplexWithValue(Value value = 0): val(value) {}
- SimplexWithValue(const Self& s):
- Parent(s), val(s.val) {}
- SimplexWithValue(const Parent& s, Value value = 0):
- Parent(s), val(value) {}
- template<class Iterator>
- SimplexWithValue(Iterator bg, Iterator end, Value value = 0):
- Parent(bg, end), val(value) {}
- SimplexWithValue(const VertexContainer& v, Value value = 0):
- Parent(v), val(value) {}
- /// @}
+ bool operator()(const Self& a, const Self& b) const
+ {
+ if (a.dimension() == b.dimension())
+ return a.vertices() < b.vertices();
+ else
+ return a.dimension() < b.dimension();
+ }
+};
- /// \name Core
- /// @{
- void set_value(Value value) { val = value; }
- Value get_value() const { return val; }
- /// @}
-
- const Self& operator=(const Self& s);
- std::ostream& operator<<(std::ostream& out) const;
+template<class V, class T>
+struct Simplex<V,T>::DataComparison
+{
+ typedef Self first_argument_type;
+ typedef Self second_argument_type;
+ typedef bool result_type;
- private:
- Value val;
-
- /* Serialization */
- friend class boost::serialization::access;
-
- template<class Archive>
- void serialize(Archive& ar, version_type );
+ bool operator()(const Self& a, const Self& b) const { return a.data() < b.data(); }
};
-/**
- * SimplexWithAttachment stores the vertex to which the simplex is attached (meant for lower-star filtrations)
- */
-template<typename V>
-class SimplexWithAttachment: public SimplexWithVertices<V>
+template<class S>
+struct DataDimensionComparison
+{
+ typedef S Simplex;
+ typedef Simplex first_argument_type;
+ typedef Simplex second_argument_type;
+ typedef bool result_type;
+
+ bool operator()(const Simplex& a, const Simplex& b) const
+ {
+ if (a.dimension() == b.dimension())
+ return a.data() < b.data();
+ else
+ return a.dimension() < b.dimension();
+ }
+};
+
+template<class V, class T>
+struct Simplex<V,T>::DataEvaluator
{
- public:
- typedef V VertexIndex;
- typedef SimplexWithVertices<VertexIndex> Parent;
-
- /// \name Constructors
- /// @{
- SimplexWithAttachment():
- attachment(VertexIndex()) {}
- template<class Iterator>
- SimplexWithAttachment(Iterator bg, Iterator end):
- Parent(bg, end) {}
- SimplexWithAttachment(const Parent& s):
- Parent(s) {}
- SimplexWithAttachment(VertexIndex vi):
- Parent(vi), attachment(vi) {}
- /// @}
+ typedef Self first_argument_type;
+ typedef Data result_type;
+
+ result_type operator()(const first_argument_type& s) const { return s.data(); }
+};
- void set_attachment(VertexIndex v) { attachment = v; }
- VertexIndex get_attachment() const { return attachment; }
-
- private:
- VertexIndex attachment;
-
- private:
- // Serialization
- friend class boost::serialization::access;
+template<class V, class T>
+struct Simplex<V,T>::DimensionExtractor
+{
+ typedef Self first_argument_type;
+ typedef Dimension result_type;
- template<class Archive>
- void serialize(Archive& ar, version_type );
+ result_type operator()(const first_argument_type& s) const { return s.dimension(); }
};
+
+// TODO: class DirectSimplex - class which stores indices of the simplices in its boundary
+// TODO: class CompactSimplex<V, T, N> - uses arrays instead of vectors to store simplices
+// (dimension N must be known at compile time)
+
+
#include "simplex.hpp"
#endif // __SIMPLEX_H__
--- a/include/topology/simplex.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/simplex.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,94 +1,128 @@
+#include <boost/serialization/serialization.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/set.hpp>
#include <boost/serialization/nvp.hpp>
+#include <boost/iterator/filter_iterator.hpp>
+#include <functional>
/* Implementations */
-/* SimplexWithVertices */
-template<class V>
-typename SimplexWithVertices<V>::Cycle
-SimplexWithVertices<V>::
-boundary() const
+template<class V, class T>
+struct Simplex<V,T>::BoundaryIterator: public boost::iterator_adaptor<BoundaryIterator, // Derived
+ typename VertexContainer::const_iterator, // Base
+ Simplex<V,T>, // Value
+ boost::use_default,
+ Simplex<V,T> >
{
- Cycle bdry;
- if (dimension() == 0) return bdry;
+ public:
+ typedef typename VertexContainer::const_iterator Iterator;
+ typedef boost::iterator_adaptor<BoundaryIterator,
+ Iterator,
+ Simplex<V,T>,
+ boost::use_default,
+ Simplex<V,T> > Parent;
+
+ BoundaryIterator() {}
+ explicit BoundaryIterator(Iterator iter, const VertexContainer& vertices):
+ Parent(iter), vertices_(vertices) {}
- for (typename VertexContainer::const_iterator cur = vertices_.begin(); cur != vertices_.end(); ++cur)
- {
- bdry.push_back(*this);
- Self& s = bdry.back();
- s.vertices_.erase(*cur);
- }
+ private:
+ friend class boost::iterator_core_access;
+ Simplex<V,T> dereference() const
+ {
+ typedef std::not_equal_to<Vertex> NotEqualVertex;
+
+ return Self(boost::make_filter_iterator(std::bind2nd(NotEqualVertex(), *(this->base())), vertices_.begin(), vertices_.end()),
+ boost::make_filter_iterator(std::bind2nd(NotEqualVertex(), *(this->base())), vertices_.end(), vertices_.end()));
+ }
- return bdry;
+ const VertexContainer& vertices_;
+};
+
+/* Simplex */
+template<class V, class T>
+typename Simplex<V,T>::BoundaryIterator
+Simplex<V,T>::
+boundary_begin() const
+{
+ if (dimension() == 0) return boundary_end();
+ return BoundaryIterator(vertices().begin(), vertices());
}
-template<class V>
-std::ostream&
-SimplexWithVertices<V>::
+template<class V, class T>
+typename Simplex<V,T>::BoundaryIterator
+Simplex<V,T>::
+boundary_end() const
+{
+ return BoundaryIterator(vertices().end(), vertices());
+}
+
+template<class V, class T>
+bool
+Simplex<V,T>::
+contains(const Vertex& v) const
+{
+ // TODO: would std::find() be faster? (since most simplices we deal with are low dimensional)
+ typename VertexContainer::const_iterator location = std::lower_bound(vertices().begin(), vertices().end(), v);
+ return ((location != vertices().end()) && (*location == v));
+}
+
+template<class V, class T>
+bool
+Simplex<V,T>::
+contains(const Self& s) const
+{
+ return std::includes( vertices().begin(), vertices().end(),
+ s.vertices().begin(), s.vertices().end());
+}
+
+template<class V, class T>
+void
+Simplex<V,T>::
+add(const Vertex& v)
+{
+ // TODO: would find() or lower_bound() followed by insert be faster?
+ vertices().push_back(v); std::sort(vertices().begin(), vertices().end());
+}
+
+template<class V, class T>
+template<class Iterator>
+void
+Simplex<V,T>::
+join(Iterator bg, Iterator end)
+{
+ vertices().insert(vertices().end(), bg, end);
+ std::sort(vertices().begin(), vertices().end());
+}
+
+template<class V, class T>
+std::ostream&
+Simplex<V,T>::
operator<<(std::ostream& out) const
{
- for (typename VertexContainer::const_iterator cur = vertices_.begin(); cur != vertices_.end(); ++cur)
- out << *cur << ' ';
-
- return out;
-}
-
-template<class V>
-template<class Archive>
-void
-SimplexWithVertices<V>::
-serialize(Archive& ar, version_type )
-{ ar & BOOST_SERIALIZATION_NVP(vertices_); }
+ typename VertexContainer::const_iterator cur = vertices().begin();
+ out << "<" << *cur;
+ for (++cur; cur != vertices().end(); ++cur)
+ {
+ out << ", " << *cur;
+ }
+ out << ">";
+ // out << " [" << data() << "] ";
-template<class V>
-std::ostream& operator<<(std::ostream& out, const SimplexWithVertices<V>& s)
-{ return s.operator<<(out); }
-
-
-/* SimplexWithValue */
-template<class V>
-std::ostream&
-SimplexWithValue<V>::
-operator<<(std::ostream& out) const
-{
- Parent::operator<<(out);
- out << "(val = " << val << ")";
- return out;
+ return out;
}
-template<class V>
-const typename SimplexWithValue<V>::Self&
-SimplexWithValue<V>::
-operator=(const Self& s)
-{
- Parent::operator=(s);
- val = s.val;
- return *this;
-}
-
-template<class V>
+template<class V, class T>
template<class Archive>
-void
-SimplexWithValue<V>::
-serialize(Archive& ar, version_type )
-{
- ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Parent);
- ar & BOOST_SERIALIZATION_NVP(val);
+void
+Simplex<V,T>::
+serialize(Archive& ar, version_type )
+{
+ ar & boost::serialization::make_nvp("vertices", vertices());
+ ar & boost::serialization::make_nvp("data", data());
}
-template<typename V>
-template<class Archive>
-void
-SimplexWithAttachment<V>::
-serialize(Archive& ar, version_type )
-{
- ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Parent);
- ar & BOOST_SERIALIZATION_NVP(attachment);
-}
-
-
-template<class V>
-std::ostream& operator<<(std::ostream& out, const SimplexWithValue<V>& s)
+template<class V, class T>
+std::ostream& operator<<(std::ostream& out, const Simplex<V,T>& s)
{ return s.operator<<(out); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/static-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,184 @@
+#ifndef __STATIC_PERSISTENCE_H__
+#define __STATIC_PERSISTENCE_H__
+
+#include "order.h"
+#include "cycles.h"
+#include "filtration.h"
+
+#include <boost/ref.hpp>
+#include <boost/lambda/lambda.hpp>
+namespace bl = boost::lambda;
+
+#include <utilities/types.h>
+
+#include <boost/progress.hpp>
+
+
+// Element_ should derive from PairCycleData
+template<class Data_, class ChainTraits_, class Element_ = use_default>
+struct PairCycleData: public Data_
+{
+ typedef Data_ Data;
+ typedef typename if_default<Element_, PairCycleData>::type Element;
+
+ typedef const Element* Index;
+ typedef typename ChainTraits_::template rebind<Index>::other ChainTraits;
+ typedef typename ChainTraits::Chain Chain;
+ typedef Chain Cycle;
+
+ PairCycleData(Index p = Index(), const Cycle& z = Cycle(), const Data& d = Data()):
+ Data(d), pair(p), cycle(z)
+ {}
+
+ bool sign() const { return cycle.empty(); }
+ bool unpaired() const { return pair == this; }
+
+ void swap_cycle(Cycle& z) { cycle.swap(z); }
+ void set_pair(Index i) { pair = i; }
+
+ Index pair;
+ Cycle cycle;
+};
+
+/**
+ * Class: StaticPersistence
+ * The class that encapsulates order and pairing information as well as
+ * implements methods to compute and maintain the pairing. Its most
+ * significant method is <pair_simplices()>.
+ *
+ * Template parameters:
+ * Data_ - auxilliary contents to store with each OrderElement
+ * OrderDescriptor_ - class describing how the order is stored; it defaults to <VectorOrderDescriptor>
+ * which serves as a prototypical class
+ */
+template<class Data_ = Empty<>,
+ class ChainTraits_ = VectorChains<>,
+ class ContainerTraits_ = OrderContainer<>,
+ class Element_ = PairCycleData<Data_, ChainTraits_>,
+ class Comparison_ = ElementComparison<typename ContainerTraits_::template rebind<Element_>::other::Container,
+ std::greater<typename ContainerTraits_::template rebind<Element_>::other::Container::iterator> > >
+class StaticPersistence
+{
+ public:
+ // Typedef: Data
+ // The data type stored in each order element
+ typedef Data_ Data;
+
+ typedef Element_ Element;
+ typedef typename ContainerTraits_::
+ template rebind<Element>::other ContainerTraits;
+ typedef typename ContainerTraits::Container Container;
+ typedef Container Order;
+ typedef typename Element::Index OrderIndex;
+ typedef Element OrderElement;
+ typedef typename Order::iterator iterator;
+
+ typedef typename ChainTraits_::
+ template rebind<OrderIndex>::other ChainTraits;
+ typedef typename ChainTraits::Chain Chain;
+ typedef Chain Cycle;
+
+ typedef Comparison_ OrderComparison;
+
+ /* Constructor: StaticPersistence() */
+ StaticPersistence(): ocmp_(order_) {}
+
+ /* Constructor: StaticPersistence()
+ * TODO: write a description
+ *
+ * Template parameters:
+ * Filtration - filtration of the complex whose persistence we are computing
+ */
+ template<class Filtration> StaticPersistence(const Filtration& f): ocmp_(order_) { initialize(f); }
+
+ // Function: initialize(const Filtration& f)
+ // Initialize the boundary map from the Filtration
+ template<class Filtration>
+ void initialize(const Filtration& f);
+
+ // Function: pair_simplices()
+ // Compute persistence of the filtration
+ void pair_simplices(bool progress = true);
+
+ // Functions: Accessors
+ // begin() - returns OrderIndex of the first element
+ // end() - returns OrderIndex of one past the last element
+ // size() - number of elements in the StaticPersistence
+ iterator begin() const { return order_.begin(); }
+ iterator end() const { return order_.end(); }
+ iterator iterator_to(OrderIndex i) const { return order_.iterator_to(*i); }
+ OrderIndex index(iterator i) const { return &*i; }
+ size_t size() const { return order_.size(); }
+ const OrderComparison& order_comparison() const { return ocmp_; }
+
+ // A map to extract simplices
+ template<class Filtration> class SimplexMap;
+ template<class Filtration>
+ SimplexMap<Filtration> make_simplex_map(const Filtration& filtration) const { return SimplexMap<Filtration>(*this, filtration); }
+
+ class OrderModifier
+ {
+ public:
+ OrderModifier(Order& order): order_(order) {}
+ template<class Functor>
+ void operator()(iterator i, const Functor& f) { order_.modify(i, f); }
+
+ private:
+ Order& order_;
+ };
+ OrderModifier modifier() { return OrderModifier(order()); }
+
+ // Function: pair_simplices(bg, end)
+ // Compute persistence of the simplices in filtration between bg and end
+ template<class Visitor>
+ void pair_simplices(iterator bg, iterator end, bool store_negative = false, const Visitor& visitor = Visitor());
+
+ // Struct: PairVisitor
+ // Acts as an archetype and if necessary a base class for visitors passed to <pair_simplices(bg, end, visitor)>.
+ struct PairVisitor
+ {
+ PairVisitor(unsigned size): show_progress(size) {}
+ // Function: init(i)
+ // Called after OrderElement pointed to by `i` has been initialized
+ // (its cycle is set to be its boundary, and pair is set to self, i.e. `i`)
+ void init(iterator i) const {}
+
+ // Function: update(j, i)
+ // Called after the cycle of `i` has been added to the cycle of `j`,
+ // this allows the derived class to perform the necessary updates
+ // (e.g., add `i`'s chain to `j`'s chain)
+ void update(iterator j, iterator i) const {}
+
+ // Function: finished(j)
+ // Called after the processing of `j` is finished.
+ void finished(iterator j) const { ++show_progress; }
+ mutable boost::progress_display
+ show_progress;
+ };
+
+ struct PairVisitorNoProgress
+ {
+ PairVisitorNoProgress() {}
+ void init(iterator i) const {}
+ void update(iterator j, iterator i) const {}
+ void finished(iterator j) const {}
+ };
+
+ protected:
+ const Order& order() const { return order_; }
+ Order& order() { return order_; }
+
+ void set_pair(iterator i, iterator j) { set_pair(i, &*j); }
+ void set_pair(iterator i, OrderIndex j) { order_.modify(i, boost::bind(&OrderElement::set_pair, bl::_1, j)); } // i->set_pair(j)
+ void set_pair(OrderIndex i, iterator j) { set_pair(iterator_to(i), &*j); }
+ void set_pair(OrderIndex i, OrderIndex j) { set_pair(iterator_to(i), j); }
+ void swap_cycle(iterator i, Cycle& z) { order_.modify(i, boost::bind(&OrderElement::swap_cycle, bl::_1, boost::ref(z))); } // i->swap_cycle(z)
+
+ private:
+ Order order_;
+ OrderComparison ocmp_;
+};
+
+#include "static-persistence.hpp"
+
+#endif // __STATIC_PERSISTENCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/static-persistence.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,156 @@
+#include <utilities/log.h>
+#include <utilities/containers.h>
+#include <utilities/property-maps.h>
+
+#include <boost/utility/enable_if.hpp>
+#include <boost/utility.hpp>
+#include <boost/foreach.hpp>
+
+#include <boost/foreach.hpp>
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlPersistence = DEF_CHANNEL("topology/persistence", rlog::Log_Debug);
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cPersistencePair = GetCounter("persistence/pair");
+static Counter* cPersistencePairBoundaries = GetCounter("persistence/pair/boundaries");
+static Counter* cPersistencePairCycleLength = GetCounter("persistence/pair/cyclelength");
+#endif // COUNTERS
+
+template<class D, class CT, class OT, class E, class Cmp>
+template<class Filtration>
+void
+StaticPersistence<D, CT, OT, E, Cmp>::
+initialize(const Filtration& filtration)
+{
+ order_.assign(filtration.size(), OrderElement());
+ rLog(rlPersistence, "Initializing persistence");
+
+ OffsetMap<typename Filtration::Index, iterator> om(filtration.begin(), begin());
+ for (typename Filtration::Index cur = filtration.begin(); cur != filtration.end(); ++cur)
+ {
+ Cycle z;
+ BOOST_FOREACH(const typename Filtration::Simplex& s, std::make_pair(cur->boundary_begin(), cur->boundary_end()))
+ z.push_back(index(om[filtration.find(s)]));
+ z.sort(ocmp_);
+
+ iterator ocur = om[cur];
+ swap_cycle(ocur, z);
+ set_pair(ocur, ocur);
+ }
+}
+
+template<class D, class CT, class OT, class E, class Cmp>
+void
+StaticPersistence<D, CT, OT, E, Cmp>::
+pair_simplices(bool progress)
+{
+ if (progress)
+ {
+ PairVisitor visitor(size());
+ pair_simplices<PairVisitor>(begin(), end(), false, visitor);
+ }
+ else
+ {
+ PairVisitorNoProgress visitor;
+ pair_simplices<PairVisitorNoProgress>(begin(), end(), false, visitor);
+ }
+}
+
+template<class D, class CT, class OT, class E, class Cmp>
+template<class Visitor>
+void
+StaticPersistence<D, CT, OT, E, Cmp>::
+pair_simplices(iterator bg, iterator end, bool store_negative, const Visitor& visitor)
+{
+#if LOGGING
+ typename ContainerTraits::OutputMap outmap(order_);
+#endif
+
+ // FIXME: need sane output for logging
+ rLog(rlPersistence, "Entered: pair_simplices");
+ for (iterator j = bg; j != end; ++j)
+ {
+ visitor.init(j);
+ rLog(rlPersistence, "Simplex %s", outmap(j).c_str());
+
+ Cycle z;
+ swap_cycle(j, z);
+ rLog(rlPersistence, " has boundary: %s", z.tostring(outmap).c_str());
+
+ // Sparsify the cycle by removing the negative elements
+ if (!store_negative)
+ {
+ typename OrderElement::Cycle zz;
+ BOOST_FOREACH(OrderIndex i, z)
+ if (i->sign()) // positive
+ zz.push_back(i);
+ z.swap(zz);
+ }
+ // --------------------------
+
+ CountNum(cPersistencePairBoundaries, z.size());
+ Count(cPersistencePair);
+
+ while(!z.empty())
+ {
+ OrderIndex i = z.top(ocmp_); // take the youngest element with respect to the OrderComparison
+ rLog(rlPersistence, " %s: %s", outmap(i).c_str(), outmap(i->pair).c_str());
+ // TODO: is this even a meaningful assert?
+ AssertMsg(!ocmp_(i, index(j)),
+ "Simplices in the cycle must precede current simplex: (%s in cycle of %s)",
+ outmap(i).c_str(), outmap(j).c_str());
+
+ // i is not paired, so we pair j with i
+ if (iterator_to(i->pair) == iterator_to(i))
+ {
+ rLog(rlPersistence, " Pairing %s and %s with cycle %s",
+ outmap(i).c_str(), outmap(j).c_str(),
+ z.tostring(outmap).c_str());
+
+ set_pair(i, j);
+ swap_cycle(j, z);
+ set_pair(j, i);
+
+ CountNum(cPersistencePairCycleLength, j->cycle.size());
+ CountBy (cPersistencePairCycleLength, j->cycle.size());
+ break;
+ }
+
+ // update element
+ z.add(i->pair->cycle, ocmp_);
+ visitor.update(j, iterator_to(i));
+ rLog(rlPersistence, " new cycle: %s", z.tostring(outmap).c_str());
+ }
+ // if z was empty, so is (already) j->cycle, so nothing to do
+ visitor.finished(j);
+ rLog(rlPersistence, "Finished with %s: %s",
+ outmap(j).c_str(), outmap(j->pair).c_str());
+ }
+}
+
+template<class D, class CT, class OT, class E, class Cmp>
+template<class Filtration>
+class StaticPersistence<D, CT, OT, E, Cmp>::SimplexMap
+{
+ public:
+ typedef OrderIndex key_type;
+ // typedef iterator key_type;
+ typedef const typename Filtration::Simplex& value_type;
+
+
+ SimplexMap(const StaticPersistence& persistence,
+ const Filtration& filtration):
+ persistence_(persistence),
+ filtration_(filtration) {}
+
+ value_type operator[](OrderIndex k) const { return (*this)[persistence_.iterator_to(k)]; }
+ value_type operator[](iterator i) const { return filtration_.simplex(filtration_.begin() + (i - persistence_.begin())); }
+
+ private:
+ const StaticPersistence& persistence_;
+ const Filtration& filtration_;
+};
+
+/* Private */
--- a/include/topology/vineyard.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/vineyard.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,6 +1,6 @@
/*
* Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
+ * Department of Computer Science, Duke University, 2005 -- 2009
*/
#ifndef __VINEYARD_H__
@@ -14,129 +14,141 @@
#include <boost/serialization/vector.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/list.hpp>
-#include <boost/serialization/is_abstract.hpp>
-
+
+#include <boost/iterator/iterator_traits.hpp>
-template<class Smplx> class Knee;
-template<class Smplx> class Vine;
+
+class Knee;
+class Vine;
/**
* Vineyard class. Keeps track of vines and knees. switched() is the key function called
- * by filtration when pairing switches after a Filtration::transpose().
+ * when pairing switches.
+ *
+ * \ingroup topology
*/
-template<class FltrSmplx>
+template<class Index_, class Iterator_, class Evaluator_>
class Vineyard
{
- public:
- typedef FltrSmplx FiltrationSimplex;
- typedef typename FiltrationSimplex::Simplex Simplex;
- typedef Vine<Simplex> Vine;
- typedef Knee<Simplex> Knee;
- typedef std::list<Vine> VineList;
- typedef std::vector<VineList> VineListVector;
- typedef typename FiltrationSimplex::Cycle Cycle;
+ public:
+ typedef Index_ Index;
+ typedef Iterator_ Iterator;
+ typedef Evaluator_ Evaluator;
- typedef typename FiltrationSimplex::Index Index;
- typedef typename FiltrationSimplex::Evaluator Evaluator;
-
- public:
- Vineyard(Evaluator* eval = 0):
- evaluator(eval) {}
+ typedef std::list<Vine> VineList;
+ typedef std::list<VineList> VineListList;
+ typedef std::vector<VineListList::iterator> VineListVector;
+
+ public:
+ Vineyard(Evaluator* eval = 0):
+ evaluator(eval) {}
- void start_vines(Index bg, Index end);
- void switched(Index i, Index j);
- void record_diagram(Index bg, Index end);
+ void start_vines(Iterator bg, Iterator end);
+ void switched(Index i, Index j);
+ template<class Iter>
+ void record_knee(Iter i);
+ void record_diagram(Iterator bg, Iterator end);
- void set_evaluator(Evaluator* eval) { evaluator = eval; }
-
- void save_edges(const std::string& filename) const;
+ void set_evaluator(Evaluator* eval) { evaluator = eval; }
- protected:
- typename Knee::SimplexList resolve_cycle(Index i) const;
+ void save_edges(const std::string& filename, bool skip_infinite = false) const;
+ void save_vines(const std::string& filename, bool skip_infinite = false) const;
- private:
- void record_knee(Index i);
- void start_vine(Index i);
+ private:
+ template<class Iter>
+ void start_vine(Iter i);
- private:
- VineListVector vines;
- Evaluator* evaluator;
+ private:
+ VineListList vines; // stores vine lists
+ VineListVector vines_vector; // stores pointers (iterators) to vine lists
+ Evaluator* evaluator;
};
/**
- * Knee class stores the knee in R^3 as well as the cycle that is associated with the Simplex starting from the Knee.
+ * Knee class stores the knee in R^3.
+ *
+ * \ingroup topology
*/
-template<class S>
class Knee
{
- public:
- typedef S Simplex;
- typedef std::list<Simplex> SimplexList;
-
- RealType birth;
- RealType death;
- RealType time;
- SimplexList cycle;
-
- // Default parameters for serialization
- Knee(RealType b = 0, RealType d = 0, RealType t = 0):
- birth(b), death(d), time(t)
- {}
- Knee(const Knee& other):
- birth(other.birth), death(other.death), time(other.time)
- {}
+ public:
+ RealType birth;
+ RealType death;
+ RealType time;
+
+ // Default parameters for serialization
+ Knee(RealType b = 0, RealType d = 0, RealType t = 0):
+ birth(b), death(d), time(t)
+ {}
+ Knee(const Knee& other):
+ birth(other.birth), death(other.death), time(other.time)
+ {}
- bool is_diagonal() const { return birth == death; }
- bool is_infinite() const { return (death == Infinity) || (birth == Infinity); }
- void set_cycle(const SimplexList& lst) { cycle = lst; }
+ bool is_diagonal() const { return birth == death; }
+ bool is_infinite() const { return (death == Infinity) || (birth == Infinity); }
- std::ostream& operator<<(std::ostream& out) const { return out << "(" << birth << ", "
- << death << ", "
- << time << ")"; }
-
- private:
- friend class boost::serialization::access;
+ std::ostream& operator<<(std::ostream& out) const { return out << "(" << birth << ", "
+ << death << ", "
+ << time << ")"; }
+
+ private:
+ friend class boost::serialization::access;
- template<class Archive>
- void serialize(Archive& ar, version_type );
+ template<class Archive>
+ void serialize(Archive& ar, version_type );
};
-template<class S>
-std::ostream& operator<<(std::ostream& out, const Knee<S>& k) { return k.operator<<(out); }
+std::ostream& operator<<(std::ostream& out, const Knee& k) { return k.operator<<(out); }
/**
* Vine is a list of Knees
*/
-template<class S>
-class Vine: public std::list<Knee<S> >
-{
- public:
- typedef S Simplex;
- typedef Knee<Simplex> Knee;
- typedef std::list<Knee> VineRepresentation;
- typedef typename VineRepresentation::const_iterator const_knee_iterator;
-
- Vine() {}
- Vine(const Knee& k) { add(k); }
-
- void add(RealType b, RealType d, RealType t) { push_back(Knee(b,d,t)); }
- void add(const Knee& k) { push_back(k); }
+class Vine: public std::list<Knee>
+{
+ public:
+ typedef std::list<Knee> VineRepresentation;
+ typedef VineRepresentation::const_iterator const_knee_iterator;
+
+ Vine() {}
+ Vine(const Vine& other):
+ VineRepresentation(other) {}
+ Vine(const VineRepresentation& other):
+ VineRepresentation(other) {}
+ Vine(const Knee& k) { add(k); }
+
+ void add(RealType b, RealType d, RealType t) { push_back(Knee(b,d,t)); }
+ void add(const Knee& k) { push_back(k); }
+
+ std::ostream& operator<<(std::ostream& out) const { std::copy(begin(), end(), std::ostream_iterator<Knee>(out, " ")); return out; }
- using VineRepresentation::begin;
- using VineRepresentation::end;
- using VineRepresentation::front;
- using VineRepresentation::back;
- using VineRepresentation::size;
- using VineRepresentation::empty;
+ using VineRepresentation::begin;
+ using VineRepresentation::end;
+ using VineRepresentation::front;
+ using VineRepresentation::back;
+ using VineRepresentation::size;
+ using VineRepresentation::empty;
+
+ protected:
+ using VineRepresentation::push_back;
+
+ private:
+ friend class boost::serialization::access;
- protected:
- using VineRepresentation::push_back;
+ template<class Archive>
+ void serialize(Archive& ar, version_type );
+};
+
+std::ostream& operator<<(std::ostream& out, const Vine& v) { return v.operator<<(out); }
+
- private:
- friend class boost::serialization::access;
+class VineData
+{
+ public:
+ void set_vine(Vine* vine) const { vine_ = vine; }
+ Vine* vine() const { return vine_; }
- template<class Archive>
- void serialize(Archive& ar, version_type );
+ private:
+ mutable Vine* vine_; // cheap trick to work around MultiIndex's constness
};
--- a/include/topology/vineyard.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/include/topology/vineyard.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -3,155 +3,188 @@
#include <fstream>
#include <sstream>
-template<class FS>
+#include "utilities/log.h"
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlVineyard = DEF_CHANNEL("topology/vineyard", rlog::Log_Debug);
+#endif // LOGGING
+
+template<class I, class It, class E>
void
-Vineyard<FS>::
-start_vines(Index bg, Index end)
+Vineyard<I,It,E>::
+start_vines(Iterator bg, Iterator end)
{
- AssertMsg(evaluator != 0, "Cannot start vines with a null evaluator");
- for (Index cur = bg; cur != end; ++cur)
- {
- if (!cur->sign()) continue;
- Dimension dim = cur->dimension();
-
- if (dim >= vines.size())
- {
- AssertMsg(dim == vines.size(), "New dimension has to be contiguous");
- vines.push_back(std::list<Vine>());
- }
+ AssertMsg(evaluator != 0, "Cannot start vines with a null evaluator");
+ for (Iterator cur = bg; cur != end; ++cur)
+ {
+ if (!cur->sign()) continue;
+ Dimension dim = evaluator->dimension(cur);
+
+ if (dim >= vines.size())
+ {
+ AssertMsg(dim == vines.size(), "New dimension has to be contiguous");
+ vines.push_back(VineList());
+ vines_vector.push_back(boost::prior(vines.end()));
+ }
- start_vine(cur);
- record_knee(cur);
- }
+ start_vine(cur);
+ record_knee(cur);
+ }
}
-template<class FS>
+template<class I, class It, class E>
void
-Vineyard<FS>::
+Vineyard<I,It,E>::
switched(Index i, Index j)
{
- Vine* i_vine = i->vine();
- Vine* j_vine = j->vine();
- i->set_vine(j_vine);
- j->set_vine(i_vine);
+ rLog(rlVineyard, "Switching vines");
+
+ Vine* i_vine = i->vine();
+ Vine* j_vine = j->vine();
+ i->set_vine(j_vine);
+ j->set_vine(i_vine);
+
+ // rLog(rlVineyard, " %x %x %x %x", i->pair, i->pair->pair, j->pair, j->pair->pair);
+ // rLog(rlVineyard, " %x %x %x %x", i_vine, i->pair->vine(), j_vine, j->pair->vine());
- // Since the pairing has already been updated, the following assertions should be true
- AssertMsg(i->vine() == i->pair()->vine(), "i's vine must match the vine of its pair");
- AssertMsg(j->vine() == j->pair()->vine(), "j's vine must match the vine of its pair");
+ // Since the pairing has already been updated, the following assertions should be true
+ AssertMsg(i->vine() == i->pair->vine(), "i's vine must match the vine of its pair");
+ AssertMsg(j->vine() == j->pair->vine(), "j's vine must match the vine of its pair");
- if (!i->sign()) i = i->pair();
- if (!j->sign()) j = j->pair();
- record_knee(i);
- record_knee(j);
+ if (!i->sign()) i = i->pair;
+ if (!j->sign()) j = j->pair;
+
+ // std::cout << "i sign: " << i->sign() << std::endl;
+ // std::cout << "j sign: " << j->sign() << std::endl;
+
+ record_knee(i);
+ record_knee(j);
}
-template<class FS>
+template<class I, class It, class E>
+template<class Iter>
void
-Vineyard<FS>::
-start_vine(Index i)
+Vineyard<I,It,E>::
+start_vine(Iter i)
{
- Dout(dc::vineyard, "Starting new vine");
- AssertMsg(i->sign(), "Can only start vines for positive simplices");
-
- Dimension dim = i->dimension();
- vines[dim].push_back(Vine());
- i->set_vine(&vines[dim].back());
- i->pair()->set_vine(i->vine());
+ rLog(rlVineyard, "Starting new vine");
+ AssertMsg(i->sign(), "Can only start vines for positive simplices");
+
+ Dimension dim = evaluator->dimension(i);
+ vines_vector[dim]->push_back(Vine());
+ i->set_vine(&vines_vector[dim]->back());
+ i->pair->set_vine(i->vine());
}
-
+
/// Records the current diagram in the vineyard
-template<class FS>
+template<class I, class It, class E>
void
-Vineyard<FS>::
-record_diagram(Index bg, Index end)
+Vineyard<I,It,E>::
+record_diagram(Iterator bg, Iterator end)
{
- Dout(dc::vineyard, "Entered: record_diagram()");
- AssertMsg(evaluator != 0, "Cannot record diagram with a null evaluator");
-
- for (Index i = bg; i != end; ++i)
- {
- AssertMsg(i->vine() != 0, "Cannot process a null vine in record_diagram");
- if (!i->sign()) continue;
- record_knee(i);
- }
+ rLog(rlVineyard, "Entered: record_diagram()");
+ AssertMsg(evaluator != 0, "Cannot record diagram with a null evaluator");
+
+ for (Iterator i = bg; i != end; ++i)
+ {
+ AssertMsg(i->vine() != 0, "Cannot process a null vine in record_diagram");
+ if (!i->sign()) continue;
+ record_knee(i);
+ }
}
-template<class FS>
-void
-Vineyard<FS>::
-save_edges(const std::string& filename) const
+template<class I, class It, class E>
+void
+Vineyard<I,It,E>::
+save_edges(const std::string& filename, bool skip_infinite) const
{
- for (unsigned int i = 0; i < vines.size(); ++i)
- {
- std::ostringstream os; os << i;
- std::string fn = filename + os.str() + ".edg";
- std::ofstream out(fn.c_str());
- for (typename VineList::const_iterator vi = vines[i].begin(); vi != vines[i].end(); ++vi)
- for (typename Vine::const_iterator ki = vi->begin(), kiprev = ki++; ki != vi->end(); kiprev = ki++)
- {
- if (kiprev->is_infinite() || ki->is_infinite()) continue;
- out << kiprev->birth << ' ' << kiprev->death << ' ' << kiprev->time << std::endl;
- out << ki->birth << ' ' << ki->death << ' ' << ki->time << std::endl;
- }
- out.close();
- }
+ for (unsigned int i = 0; i < vines_vector.size(); ++i)
+ {
+ std::ostringstream os; os << i;
+ std::string fn = filename + os.str() + ".edg";
+ std::ofstream out(fn.c_str());
+ for (typename VineList::const_iterator vi = vines_vector[i]->begin(); vi != vines_vector[i]->end(); ++vi)
+ for (typename Vine::const_iterator ki = vi->begin(), kiprev = ki++; ki != vi->end(); kiprev = ki++)
+ {
+ if (skip_infinite && (kiprev->is_infinite() || ki->is_infinite()))
+ {
+ std::cerr << "Warning: skipping an infinite knee in save_edges() in dimension " << i << std::endl;
+ continue;
+ }
+ out << kiprev->birth << ' ' << kiprev->death << ' ' << kiprev->time << std::endl;
+ out << ki->birth << ' ' << ki->death << ' ' << ki->time << std::endl;
+ }
+ out.close();
+ }
+}
+
+template<class I, class It, class E>
+void
+Vineyard<I,It,E>::
+save_vines(const std::string& filename, bool skip_infinite) const
+{
+ for (unsigned int i = 0; i < vines_vector.size(); ++i)
+ {
+ std::ostringstream os; os << i;
+ std::string fn = filename + os.str() + ".vin";
+ std::ofstream out(fn.c_str());
+ for (typename VineList::const_iterator vi = vines_vector[i]->begin(); vi != vines_vector[i]->end(); ++vi)
+ {
+ for (typename Vine::const_iterator ki = vi->begin(); ki != vi->end(); ki++)
+ {
+ if (skip_infinite && ki->is_infinite())
+ {
+ std::cerr << "Warning: skipping an infinite knee in save_edges() in dimension " << i << std::endl;
+ continue;
+ }
+ out << ki->birth << ' ' << ki->death << ' ' << ki->time << " ";
+ }
+ out << std::endl;
+ }
+ out.close();
+ }
}
/// Records a knee for the given simplex
-template<class FS>
+template<class I, class It, class E>
+template<class Iter>
void
-Vineyard<FS>::
-record_knee(Index i)
+Vineyard<I,It,E>::
+record_knee(Iter i)
{
- Dout(dc::vineyard, "Entered record_knee()");
- AssertMsg(evaluator != 0, "Cannot record knee with a null evaluator");
- AssertMsg(i->vine() != 0, "Cannot add a knee to a null vine");
- AssertMsg(i->sign(), "record_knee() must be called on a positive simplex");
-
- if (!i->is_paired())
- i->vine()->add(evaluator->value(*i), Infinity, evaluator->time());
- else
- {
- Dout(dc::vineyard, "Creating knee");
- Knee k(evaluator->value(*i), evaluator->value(*(i->pair())), evaluator->time());
- Dout(dc::vineyard, "Knee created: " << k);
+ rLog(rlVineyard, "Entered record_knee()");
+ AssertMsg(evaluator != 0, "Cannot record knee with a null evaluator");
+ AssertMsg(i->vine() != 0, "Cannot add a knee to a null vine");
+ AssertMsg(i->sign(), "record_knee() must be called on a positive simplex");
+
+ if (i->unpaired())
+ i->vine()->add((*evaluator)(i), Infinity, evaluator->time());
+ else
+ {
+ rLog(rlVineyard, "Creating knee");
+ Knee k((*evaluator)(i), (*evaluator)((i->pair)), evaluator->time());
+ rLog(rlVineyard, "Knee created: %s", tostring(k).c_str());
+ rLog(rlVineyard, "Vine: %s", tostring(*(i->vine())).c_str());
- if (!k.is_diagonal() || i->vine()->empty()) // non-diagonal k, or empty vine
- {
- Dout(dc::vineyard, "Extending a vine");
- i->vine()->add(k);
- }
- else if (i->vine()->back().is_diagonal()) // last knee is diagonal
- {
- AssertMsg(i->vine()->size() == 1, "Only first knee may be diagonal for a live vine");
- Dout(dc::vineyard, "Overwriting first diagonal knee");
- i->vine()->back() = k;
- } else // finish this vine
- {
- Dout(dc::vineyard, "Finishing a vine");
- i->vine()->add(k);
- start_vine(i);
- i->vine()->add(k);
- }
- }
-
- i->vine()->back().set_cycle(resolve_cycle(i));
- Dout(dc::vineyard, "Leaving record_knee()");
+ if (!k.is_diagonal() || i->vine()->empty()) // non-diagonal k, or empty vine
+ {
+ rLog(rlVineyard, "Extending a vine");
+ i->vine()->add(k);
+ }
+ else if (i->vine()->back().is_diagonal()) // last knee is diagonal
+ {
+ AssertMsg(i->vine()->size() == 1, "Only first knee may be diagonal for a live vine");
+ rLog(rlVineyard, "Overwriting first diagonal knee");
+ i->vine()->back() = k;
+ } else // finish this vine
+ {
+ rLog(rlVineyard, "Finishing a vine");
+ i->vine()->add(k);
+ start_vine(i);
+ i->vine()->add(k);
+ }
+ }
+
+ rLog(rlVineyard, "Leaving record_knee()");
}
-
-template<class FS>
-typename Vineyard<FS>::Knee::SimplexList
-Vineyard<FS>::
-resolve_cycle(Index i) const
-{
- Dout(dc::vineyard, "Entered resolve_cycle");
- const Cycle& cycle = i->cycle();
-
- // Resolve simplices
- typename Knee::SimplexList lst;
- for (typename Cycle::const_iterator cur = cycle.begin(); cur != cycle.end(); ++cur)
- lst.push_back(**cur);
-
- return lst;
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/weighted-rips.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,139 @@
+#ifndef __WEIGHTED_RIPS_H__
+#define __WEIGHTED_RIPS_H__
+
+#include <vector>
+#include <string>
+#include "simplex.h"
+#include "rips.h"
+#include <boost/iterator/counting_iterator.hpp>
+
+/**
+ * WeightedRipsSimplex class
+ *
+ * This class sits as an invisible layer between the Simplex datatype passed
+ * to WeightedRips and the class itself. The need for this layer is the need
+ * to store the ``value'' (max inter-vertex distance) of each simplex in the
+ * Weighted Rips complex--something that the user of the class does not need
+ * to be aware of.
+ */
+
+template<class Simplex_, class DistanceType_>
+class WeightedRipsSimplex : public Simplex_
+{
+ public:
+ typedef typename Simplex_::Vertex Vertex;
+ typedef typename Simplex_::VertexContainer VertexContainer;
+ typedef DistanceType_ DistanceType;
+
+ WeightedRipsSimplex(Simplex_ s) : Simplex_(s) { }
+
+ void setSimplexValue(const DistanceType &sv) { simplexValue = sv; }
+ DistanceType getSimplexValue() const { return simplexValue; }
+
+ protected:
+ DistanceType simplexValue;
+};
+
+/**
+ * WeightedRips class
+ *
+ * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm,
+ * and provides simple wrappers for various functions.
+ *
+ * Distances_ is expected to define types IndexType and DistanceType as well as
+ * provide operator()(...) which given two IndexTypes should return
+ * the distance between them. There should be methods begin() and end()
+ * for iterating over IndexTypes as well as a method size().
+ */
+template<class Distances_, class Simplex_ = Simplex<typename Distances_::IndexType> >
+class WeightedRips : public Rips<Distances_, Simplex_>
+{
+ public:
+
+ /* redeclaring the typedefs because they cannot be inherited at compile-time */
+ typedef Distances_ Distances;
+ typedef typename Distances::IndexType IndexType;
+ typedef typename Distances::DistanceType DistanceType;
+
+ typedef WeightedRipsSimplex<Simplex_, DistanceType> Simplex;
+ typedef typename Simplex::Vertex Vertex; // should be the same as IndexType
+ typedef typename Simplex::VertexContainer VertexContainer;
+
+ class Evaluator;
+ class Comparison;
+
+ public:
+ WeightedRips(const Distances& distances):
+ Rips<Distances_, Simplex_>(distances) {}
+
+ template<class Functor>
+ void generate(Dimension k, DistanceType max, const Functor& f) const;
+
+};
+
+/**
+ * DistanceDataStackingFunctor class
+ *
+ * Class providing a functor that is to be called by WeightedRips::generate(). This functor
+ * takes as an argument (to its constructor) the original functor passed by the user to
+ * generate(), and a new ``double'' functor is created. Assuming that the functor acts on
+ * simplices, first the value of the simplex is computed (the radius at which the simplex
+ * appears in the weighted Rips complex), the data field of the simplex is populated with
+ * this value, and then the original functor is called (it has no idea that it was
+ * intercepted).
+ */
+
+template<class Rips_, class Functor_>
+class DistanceDataStackingFunctor
+{
+ public:
+ typedef typename Rips_::Simplex Simplex_;
+
+ DistanceDataStackingFunctor(const Rips_ &r, const Functor_ &f):
+ rips(r), original_functor(f) { }
+
+ void operator()(const Simplex_ &s) const
+ {
+ Simplex_ s_new(s);
+ s_new.setSimplexValue (rips.distance(s_new, s_new));
+ original_functor (s_new);
+ }
+
+ private:
+ const Rips_ &rips;
+ const Functor_ &original_functor;
+};
+
+template<class Distances_, class Simplex_>
+template<class Functor>
+void WeightedRips<Distances_, Simplex_>::generate(Dimension k, DistanceType max, const Functor &f) const
+{
+ Rips<Distances_,Simplex_>::generate(k, max, DistanceDataStackingFunctor<WeightedRips<Distances_, Simplex_>,Functor>(*this, f));
+}
+
+template<class Distances_, class Simplex_>
+class WeightedRips<Distances_, Simplex_>::Evaluator: public Rips<Distances_,Simplex_>::Evaluator
+{
+ public:
+ Evaluator(const Distances& distances):
+ Rips<Distances_, Simplex_>::Evaluator(distances) {}
+
+ DistanceType operator()(const Simplex& s) const { return s.getSimplexValue(); }
+};
+
+template<class Distances_, class Simplex_>
+class WeightedRips<Distances_, Simplex_>::Comparison: public Rips<Distances_,Simplex_>::Comparison
+{
+ public:
+ Comparison(const Distances& distances):
+ Rips<Distances_, Simplex_>::Comparison(distances) {}
+
+ bool operator()(const Simplex& s1, const Simplex& s2) const
+ {
+ if (s1.dimension() != s2.dimension())
+ return s1.dimension() < s2.dimension();
+ return s1.getSimplexValue() < s2.getSimplexValue();
+ }
+};
+
+#endif // __WEIGHTED_RIPS_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/zigzag-persistence.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,193 @@
+#ifndef __ZIGZAG_PERSISTENCE_H__
+#define __ZIGZAG_PERSISTENCE_H__
+
+#include "cycles.h"
+#include "utilities/types.h"
+#include <sstream>
+
+#if DEBUG_CONTAINERS
+ #include <debug/list>
+ using std::__debug::list;
+ #warning "Using debug/list in ZigzagPersistence"
+#else
+ #include <list>
+ using std::list;
+#endif
+
+
+/**
+ * Class: ZigzagPersistence
+ * TODO: this should probably be parametrized by Chain or Field
+ */
+template<class BirthID_ = Empty<>, class SimplexData_ = Empty<> >
+class ZigzagPersistence
+{
+ public:
+ typedef BirthID_ BirthID;
+ typedef SimplexData_ SimplexData;
+
+ struct ZNode;
+ struct BNode;
+ struct SimplexNode;
+
+ typedef list<ZNode> ZList;
+ typedef typename ZList::iterator ZIndex;
+ typedef list<BNode> BList;
+ typedef typename BList::iterator BIndex;
+ typedef list<SimplexNode> SimplexList;
+ typedef typename SimplexList::iterator SimplexIndex;
+
+ // TODO: should all chains be DequeChains? probably not
+ typedef typename DequeChains<ZIndex>::Chain ZRow;
+ typedef typename DequeChains<ZIndex>::Chain BColumn;
+ typedef typename VectorChains<BIndex>::Chain BRow;
+ typedef typename VectorChains<BIndex>::Chain CRow;
+ typedef typename VectorChains<SimplexIndex>::Chain ZColumn;
+ typedef typename VectorChains<SimplexIndex>::Chain CColumn;
+
+ typedef boost::optional<BirthID> Death;
+ typedef std::pair<SimplexIndex, Death> IndexDeathPair;
+
+ struct ZNode
+ {
+ ZNode(int o, BIndex l):
+ order(o), low(l) {}
+
+ int order;
+ ZColumn z_column;
+ BRow b_row;
+ BIndex low; // which BColumn has this ZIndex as low
+
+ BirthID birth; // TODO: do we need to do empty-member optimization?
+ // i.e., does it ever make sense for birth to be empty?
+ };
+
+ struct BNode
+ {
+ BNode(unsigned o): order(o) {}
+
+ unsigned order;
+ BColumn b_column;
+ CColumn c_column;
+ };
+
+ struct SimplexNode: public SimplexData
+ {
+ SimplexNode(unsigned o, ZIndex l):
+ order(o), low(l) {}
+
+ unsigned order;
+ ZRow z_row;
+ CRow c_row;
+ ZIndex low; // which ZColumn has this SimplexNode as low
+#if ZIGZAG_CONSISTENCY
+ ZColumn boundary; // NB: debug only
+#endif
+ };
+
+ // Constructor: ZigzagPersistence()
+ ZigzagPersistence() {}
+
+ // Function: add(bdry, birth)
+ IndexDeathPair add(ZColumn bdry, const BirthID& birth = BirthID()) { ZigzagVisitor zzv; return add<ZigzagVisitor>(bdry, birth, zzv); }
+
+ // Function: remove(s, birth)
+ Death remove(SimplexIndex s, const BirthID& birth = BirthID()) { ZigzagVisitor zzv; return remove<ZigzagVisitor>(s, birth, zzv); }
+
+
+ ZIndex begin() { return z_list.begin(); }
+ ZIndex end() { return z_list.end(); }
+
+ bool is_alive(ZIndex i) const { return i->low == b_list.end(); }
+ bool is_alive(ZNode zn) const { return zn.low == b_list.end(); }
+
+ protected:
+ // Function: add(s)
+ template<class Visitor>
+ IndexDeathPair add(ZColumn bdry, const BirthID& birth, Visitor& visitor);
+
+ // Function: remove(s)
+ template<class Visitor>
+ Death remove(SimplexIndex s, const BirthID& birth, Visitor& visitor);
+
+ // Struct: ZigzagVisitor
+ // Various methods of an instance of this class are called at different stages of addition and removal algorithm.
+ // NB: currently the places where it's called are catered for image zigzags, in the future this could be expanded
+ // to provide simple support for other algorithms
+ // TODO: not obvious that the methods should be const (and therefore the reference passed to add() and remove())
+ // revisit when working on ImageZigzag
+ struct ZigzagVisitor
+ {
+ SimplexIndex new_simplex(ZigzagPersistence& zz);
+
+ // Function: new_z_in_add(zz, z, u)
+ // Called when a new cycle is born after adding a simplex. The method is expected to add an element to z_list, and return its ZIndex.
+ ZIndex new_z_in_add(ZigzagPersistence& zz, const ZColumn& z, const BRow& u);
+
+ BIndex select_j_in_remove(ZigzagPersistence& zz, const CRow& c_row);
+
+ ZIndex new_z_in_remove(ZigzagPersistence& zz);
+
+ void erasing_z(ZigzagPersistence&, ZIndex) {}
+
+ Death death(ZigzagPersistence& zz, ZIndex dying_z);
+ };
+
+ public:
+ // Debug; non-const because Indices are iterators, and not const_iterators
+ void show_all();
+ bool check_consistency(SimplexIndex s_skip, ZIndex z_skip, BIndex b_skip);
+ bool check_consistency() { return check_consistency(s_list.end(), z_list.end(), b_list.end()); }
+
+ protected:
+ ZList z_list;
+ BList b_list;
+ SimplexList s_list;
+
+ /* Helper functors */
+ template<class Member, class Element> struct Appender;
+ template<class Member, class Element> struct Remover;
+ template<class Member, class Chain> struct Adder;
+
+ template<class Member, class Element>
+ Appender<Member, Element> make_appender(Member m, Element e) const { return Appender<Member, Element>(m,e); }
+ template<class Member, class Element>
+ Remover<Member, Element> make_remover(Member m, Element e) const { return Remover<Member, Element>(m,e); }
+ template<class Member, class Chain>
+ Adder<Member, Chain> make_adder(Member m, Chain& c) const { return Adder<Member, Chain>(m, c); }
+
+ template<class Index, class IndexFrom, class PrimaryMember, class SecondaryMember>
+ void add_chains(Index bg, Index end, IndexFrom j, PrimaryMember pm, SecondaryMember sm);
+ template<class IndexTo, class IndexFrom, class PrimaryMemberTo, class SecondaryMemberTo, class PrimaryMemberFrom>
+ void add_chains(IndexTo bg, IndexTo end, IndexFrom j,
+ PrimaryMemberTo pmt, SecondaryMemberTo smt,
+ PrimaryMemberFrom pmf);
+ template<class Index, class PrimaryMember, class SecondaryMember>
+ void add_chain(Index to, Index from,
+ PrimaryMember pmt, SecondaryMember smt);
+ template<class IndexTo, class IndexFrom, class PrimaryMemberTo, class SecondaryMemberTo, class PrimaryMemberFrom>
+ void add_chain(IndexTo to, IndexFrom from,
+ PrimaryMemberTo pmt, SecondaryMemberTo smt,
+ PrimaryMemberFrom pmf);
+ template<class IndexTo, class IndexFrom, class PrimaryMember, class SecondaryMember, class DualPrimaryMember, class DualSecondaryMember>
+ void change_basis(IndexTo bg, IndexTo end, IndexFrom j,
+ PrimaryMember pm, SecondaryMember sm,
+ DualPrimaryMember dpm, DualSecondaryMember dsm);
+
+ public:
+ struct OrderComparison
+ {
+ template<class T>
+ bool operator()(T a, T b) const { return a->order < b->order; }
+ } cmp;
+
+ struct OrderOutput
+ {
+ template<class T>
+ std::string operator()(T a) const { std::stringstream s; s << a->order; return s.str(); }
+ } out;
+};
+
+#include "zigzag-persistence.hpp"
+
+#endif // __ZIGZAG_PERSISTENCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/topology/zigzag-persistence.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,724 @@
+#include <utilities/log.h>
+#include <boost/utility.hpp>
+#include <boost/iterator/filter_iterator.hpp>
+#include <algorithm>
+#include <utilities/indirect.h>
+#include <functional>
+
+#ifdef LOGGING
+static rlog::RLogChannel* rlZigzagAdd = DEF_CHANNEL("topology/persistence/zigzag/add", rlog::Log_Debug);
+static rlog::RLogChannel* rlZigzagRemove = DEF_CHANNEL("topology/persistence/zigzag/remove", rlog::Log_Debug);
+static rlog::RLogChannel* rlZigzagAddChain = DEF_CHANNEL("topology/persistence/zigzag/addchain", rlog::Log_Debug);
+static rlog::RLogChannel* rlZigzagCheckConsistency= DEF_CHANNEL("topology/persistence/zigzag/check", rlog::Log_Debug);
+#endif // LOGGING
+
+#ifdef COUNTERS
+static Counter* cZigzagAdd = GetCounter("zigzag/add");
+static Counter* cZigzagRemove = GetCounter("zigzag/remove");
+static Counter* cZigzagConsistency = GetCounter("zigzag/consistency");
+#endif // COUNTERS
+
+template<class BID, class SD>
+template<class Visitor>
+typename ZigzagPersistence<BID,SD>::IndexDeathPair
+ZigzagPersistence<BID,SD>::
+add(ZColumn bdry, const BirthID& birth, Visitor& visitor)
+{
+ Count(cZigzagAdd);
+
+ rLog(rlZigzagAdd, "Entered ZigzagPersistence::add()");
+ rLog(rlZigzagAdd, " Boundary: %s", bdry.tostring(out).c_str());
+ rLog(rlZigzagAdd, " Boundary size: %d", bdry.size());
+ AssertMsg(check_consistency(), "Must be consistent before addition");
+
+ SimplexIndex last_s = visitor.new_simplex(*this);
+ last_s->low = z_list.end();
+#if ZIGZAG_CONSISTENCY
+ last_s->boundary = bdry; // NB: debug only
+#endif
+
+ rLog(rlZigzagAdd, " Reducing among cycles");
+ // Reduce bdry among the cycles
+ rLog(rlZigzagAdd, " Boundary: %s", bdry.tostring(out).c_str());
+ BColumn v; // representation of the boundary in the cycle basis
+ while (!bdry.empty())
+ {
+ SimplexIndex l = bdry.back();
+ ZIndex k = l->low;
+ v.append(k, cmp);
+ bdry.add(k->z_column, cmp);
+ rLog(rlZigzagAdd, " Boundary: %s", bdry.tostring(out).c_str());
+ }
+ rLog(rlZigzagAdd, " Reduced among cycles");
+
+ // Reduce v among boundaries
+ BRow u;
+ while (!(v.empty()))
+ {
+ ZIndex l = v.back();
+ BIndex k = l->low;
+
+ if (k == b_list.end())
+ break;
+
+ u.append(k, cmp);
+ v.add(k->b_column, cmp);
+ }
+ rLog(rlZigzagAdd, " Reduced among boundaries");
+
+ if (v.empty())
+ {
+ rLog(rlZigzagAdd, " Birth case in add");
+
+ // Figure out the new cycle z
+ ZColumn z;
+ std::for_each(u.begin(), u.end(), make_adder(&BNode::c_column, z));
+ z.append(last_s, cmp);
+
+ // Birth
+ ZIndex new_z = visitor.new_z_in_add(*this, z, u);
+ new_z->birth = birth;
+
+ // Set s_row
+ std::for_each(z.begin(), z.end(), make_appender(&SimplexNode::z_row, new_z));
+
+ // Set z_column
+ new_z->z_column.swap(z);
+
+ // Set low
+ new_z->low = b_list.end();
+ last_s->low = new_z;
+
+ return std::make_pair(last_s, Death());
+ } else
+ {
+ rLog(rlZigzagAdd, " Death case in add");
+
+ // Death
+ unsigned order = b_list.empty() ? 0 : boost::prior(b_list.end())->order + 1;
+ b_list.push_back(BNode(order));
+ BIndex last_b = boost::prior(b_list.end());
+
+ // Set b_column and low
+ last_b->b_column.swap(v);
+ last_b->b_column.back()->low = last_b;
+
+ // Set b_row
+ std::for_each(last_b->b_column.begin(), last_b->b_column.end(), make_appender(&ZNode::b_row, last_b));
+
+ // Set c_column
+ CColumn& c = last_b->c_column;
+ std::for_each(u.begin(), u.end(), make_adder(&BNode::c_column, c));
+ c.append(last_s, cmp);
+
+ // Set c_row
+ std::for_each(c.begin(), c.end(), make_appender(&SimplexNode::c_row, last_b));
+
+ return std::make_pair(last_s, visitor.death(*this, last_b->b_column.back()));
+ }
+}
+
+
+template<class BID, class SD>
+template<class Visitor>
+typename ZigzagPersistence<BID,SD>::Death
+ZigzagPersistence<BID,SD>::
+remove(SimplexIndex s, const BirthID& birth, Visitor& visitor)
+{
+ Count(cZigzagRemove);
+
+ rLog(rlZigzagRemove, "Entered ZigzagPersistence::remove(%d)", s->order);
+ AssertMsg(check_consistency(), "Must be consistent before removal");
+
+ if (s->z_row.empty())
+ {
+ AssertMsg(!(s->c_row.empty()), "Birth after removal, row in C must be non-empty");
+
+ // Birth
+ //show_all();
+ rLog(rlZigzagRemove, "Birth case in remove");
+
+ // Prepend DC[j] = ZB[j] to Z
+ rLog(rlZigzagRemove, "Computing the column DC[j] = ZB[j] to prepend to Z");
+ BIndex j = visitor.select_j_in_remove(*this, s->c_row);
+ rLog(rlZigzagRemove, " j = %d", j->order);
+
+ ZColumn z;
+ std::for_each(j->b_column.begin(), j->b_column.end(), make_adder(&ZNode::z_column, z));
+
+ ZIndex first_z = visitor.new_z_in_remove(*this);
+ first_z->birth = birth;
+ std::for_each(z.begin(), z.end(), make_appender(&SimplexNode::z_row, first_z));
+ first_z->z_column.swap(z);
+ first_z->low = b_list.end();
+
+ rLog(rlZigzagRemove, " Prepended %d [%s]", first_z->order, z.tostring(out).c_str());
+ //AssertMsg(check_consistency(), "Must be consistent after prepending DC[j] = ZB[j] to Z");
+
+ // Prepend row of s in C to B
+ rLog(rlZigzagRemove, "Prepending the row of s to B");
+ first_z->b_row = s->c_row; // copying instead of swapping is inefficient,
+ // but it simplifies logic when subtracting chains later
+ std::for_each(first_z->b_row.begin(), first_z->b_row.end(), make_appender(&BNode::b_column, first_z));
+ //AssertMsg(check_consistency(), "Must be consistent after prepending row of s to B");
+
+#if ZIGZAG_CONSISTENCY
+ {
+ ZColumn zz;
+ std::for_each(j->b_column.begin(), j->b_column.end(), make_adder(&ZNode::z_column, zz));
+ AssertMsg(zz.empty(), "ZB[j] must be 0 after we prepended the row of s in C to B");
+ }
+#endif
+
+ typedef std::not_equal_to<BIndex> NotEqualBIndex;
+
+ // Subtract C[j] from every column of C that contains s
+ AssertMsg(s->c_row == first_z->b_row, "s->c_row == first_z->b_row before subtracting C[j]");
+ rLog(rlZigzagRemove, "Subtracting C[j]=[%s] from every column of C that contains s=%d with row [%s]",
+ j->c_column.tostring(out).c_str(),
+ s->order, s->c_row.tostring(out).c_str());
+ add_chains(boost::make_filter_iterator(std::bind2nd(NotEqualBIndex(), j), first_z->b_row.begin(), first_z->b_row.end()),
+ boost::make_filter_iterator(std::bind2nd(NotEqualBIndex(), j), first_z->b_row.end(), first_z->b_row.end()),
+ j, &BNode::c_column, &SimplexNode::c_row);
+ add_chain(j, j, &BNode::c_column, &SimplexNode::c_row);
+ // TODO: that's how it was done before, now it can be removed
+ // add_chains(first_z->b_row.rbegin(), first_z->b_row.rend(), j, &BNode::c_column, &SimplexNode::c_row);
+ //AssertMsg(check_consistency(s_list.end(), z_list.begin(), b_list.end()), "Must be consistent after subtracting C[j] in remove::birth");
+
+ // Subtract B[j] from every other column of B that has l
+ ZIndex l = j->b_column.back();
+ BRow l_row = l->b_row;
+ rLog(rlZigzagRemove, "Subtracting B[j], j is %d, l is %d, l_row: [%s]",
+ j->order, l->order, l_row.tostring(out).c_str());
+ add_chains(boost::make_filter_iterator(std::bind2nd(NotEqualBIndex(), j), l_row.rbegin(), l_row.rend()),
+ boost::make_filter_iterator(std::bind2nd(NotEqualBIndex(), j), l_row.rend(), l_row.rend()),
+ j, &BNode::b_column, &ZNode::b_row);
+ j->b_column.back()->low = b_list.end(); // redundant since l will be deleted (here for check_consistency only)
+ add_chain(j, j, &BNode::b_column, &ZNode::b_row);
+ // TODO: investigate why this works for ordinary zigzag, but fails for the image zigzag
+ //AssertMsg(check_consistency(s_list.end(), first_z, b_list.end()), "Must be consistent after subtracting B[j] in remove::birth");
+
+
+ // Drop j, l, and s
+ //
+ // l->z_column is the only non-empty thing, but we drop it,
+ // the basis is preserved because we added first_z
+ l->z_column.back()->low = z_list.end();
+ std::for_each(l->z_column.begin(), l->z_column.end(), make_remover(&SimplexNode::z_row, l));
+
+ //show_all();
+ rLog(rlZigzagRemove, "l=%d has z_column: [%s]", l->order, l->z_column.tostring(out).c_str());
+
+ AssertMsg(l->b_row.empty(), "b_row of l must be empty before erasing in remove::birth");
+ AssertMsg(s->z_row.empty(), "z_row of s must be empty before erasing in remove::birth");
+ rLog(rlZigzagRemove, "s->c_row: [%s]", s->c_row.tostring(out).c_str());
+ if (!s->c_row.empty())
+ {
+ rLog(rlZigzagRemove, "s->c_row[0]: [%s]", s->c_row.front()->c_column.tostring(out).c_str());
+ rLog(rlZigzagRemove, " b_column: [%s]", s->c_row.front()->b_column.tostring(out).c_str());
+ }
+ AssertMsg(s->c_row.empty(), "c_row of s must be empty before erasing in remove::birth");
+ visitor.erasing_z(*this, l);
+ b_list.erase(j);
+ z_list.erase(l);
+ s_list.erase(s);
+ AssertMsg(check_consistency(s_list.end(), first_z, b_list.end()), "Must be consistent before reducing Z in remove::birth");
+
+ // Reduce Z
+ rLog(rlZigzagRemove, "Reducing Z");
+ SimplexIndex ls = first_z->z_column.back();
+ while(ls->low != first_z)
+ {
+ if (ls->low == z_list.end()) { ls->low = first_z; break; }
+
+ // if ls->low precedes first_z, swap them
+ if (cmp(ls->low, first_z)) std::swap(ls->low, first_z);
+
+ add_chain(first_z, ls->low, &ZNode::b_row, &BNode::b_column);
+ add_chain(ls->low, first_z, &ZNode::z_column, &SimplexNode::z_row);
+ std::swap(ls->low, first_z);
+
+ ls = first_z->z_column.back();
+ }
+ AssertMsg(check_consistency(), "Must be consistent at the end of birth case in remove");
+
+ return Death();
+ } else
+ {
+ // Death
+ rLog(rlZigzagRemove, "Death case in remove");
+
+ ZIndex j = s->z_row.front();
+ CRow c_row = s->c_row;
+
+ rLog(rlZigzagRemove, "j=%d, j->b_row=[%s]", j->order, j->b_row.tostring(out).c_str());
+ rLog(rlZigzagRemove, "s=%d, s->c_row=[%s]", s->order, s->c_row.tostring(out).c_str());
+ rLog(rlZigzagRemove, "s=%d, s->z_row=[%s]", s->order, s->z_row.tostring(out).c_str());
+
+ // Subtract Z[j] from every chain in C that contains s
+ // (it's Ok to go in the forward order since we are subtracting a column in Z from C)
+ add_chains(c_row.begin(), c_row.end(), j, &BNode::c_column, &SimplexNode::c_row, &ZNode::z_column);
+ AssertMsg(check_consistency(), "Must be consistent after subtracting Z[j] from C");
+
+ // Change basis to remove s from Z
+ // Compute reducers --- columns that we will be adding to other columns
+ ZRow z_row = s->z_row;
+ typedef typename ZRow::reverse_iterator ZRowReverseIterator;
+ typedef std::list<ZRowReverseIterator> ReducersContainer;
+ ReducersContainer reducers; // list of ZColumns that we will be adding to other columns
+ reducers.push_back(boost::prior(z_row.rend())); // j is the first reducer
+ AssertMsg(*(reducers.back()) == j, "The first element of reducers should be j");
+ SimplexIndex low = j->z_column.back();
+ rLog(rlZigzagRemove, " Added reducer %d [%s] with low=%d",
+ j->order, j->z_column.tostring(out).c_str(),
+ low->order);
+ for (typename ZRow::iterator cur = z_row.begin(); cur != z_row.end(); ++cur)
+ if (cmp((*cur)->z_column.back(), low))
+ {
+ reducers.push_back(ZRowReverseIterator(boost::next(cur)));
+ low = (*(reducers.back()))->z_column.back();
+ rLog(rlZigzagRemove, " Added reducer %d [%s] with low=%d",
+ (*cur)->order, (*cur)->z_column.tostring(out).c_str(),
+ low->order);
+ rLog(rlZigzagRemove, " reducers.back(): %d [%s] with low=%d",
+ (*(reducers.back()))->order,
+ (*(reducers.back()))->z_column.tostring(out).c_str(),
+ (*(reducers.back()))->z_column.back()->order);
+ }
+ rLog(rlZigzagRemove, " Reducers size: %d, s is %d",
+ reducers.size(), s->order);
+
+ //show_all();
+
+ // Add each reducer to the columns that follow them until the next reducer
+ // NB: processing reducers in the reverse order fixes a bug in the paper,
+ // in step Remove.Death.1.4, where the matrix B is updated incorrectly.
+ // I can't find a mention of this bug in my notes, but the fact
+ // that it's fixed in the code suggests that I knew about it. (Or
+ // most likely I didn't recognize that what the paper said is not
+ // exactly what I meant.)
+ typename ReducersContainer::reverse_iterator cur = reducers.rbegin();
+ ZRowReverseIterator zcur = z_row.rbegin();
+
+ while (cur != reducers.rend())
+ {
+ rLog(rlZigzagRemove, " Cur reducer: %d [%s]", (**cur)->order,
+ (**cur)->z_column.tostring(out).c_str());
+ change_basis(zcur, *cur, **cur,
+ &ZNode::z_column, &SimplexNode::z_row,
+ &ZNode::b_row, &BNode::b_column);
+ if (cur != reducers.rbegin())
+ {
+ AssertMsg((*zcur)->z_column.back() == (**cur)->z_column.back(),
+ "The back of the z_columns must be the same.");
+ (*zcur)->z_column.back()->low = *zcur;
+ }
+ else
+ (**cur)->z_column.back()->low = z_list.end();
+
+ zcur = *cur++;
+ // This makes it inconsistent until the next iteration of this update loop
+ }
+
+ // Drop j and s
+ Death d = visitor.death(*this, j);
+
+ if (j->z_column.back()->low == j)
+ j->z_column.back()->low = z_list.end();
+ std::for_each(j->z_column.begin(), j->z_column.end(), make_remover(&SimplexNode::z_row, j));
+ rLog(rlZigzagRemove, "j->b_row: [%s]", j->b_row.tostring(out).c_str());
+ if (!j->b_row.empty())
+ {
+ rLog(rlZigzagRemove, "j->b_row[0]: [%s]", j->b_row.front()->b_column.tostring(out).c_str());
+ rLog(rlZigzagRemove, " c_column: [%s]", j->b_row.front()->c_column.tostring(out).c_str());
+ }
+ AssertMsg(j->b_row.empty(), "b_row of j must be empty before erasing in remove(). Most likely you are trying to remove a simplex whose coface is still in the complex.");
+ AssertMsg(s->z_row.empty(), "z_row of s must be empty before erasing in remove()");
+ AssertMsg(s->c_row.empty(), "c_row of s must be empty before erasing in remove()");
+ visitor.erasing_z(*this, j);
+ z_list.erase(j);
+ s_list.erase(s);
+
+ //show_all();
+
+ AssertMsg(check_consistency(), "Must be consistent when done in remove()");
+
+ return d;
+ }
+}
+
+template<class BID, class SD>
+void
+ZigzagPersistence<BID,SD>::
+show_all()
+{
+ std::cout << "s_list:" << std::endl;
+ for (SimplexIndex cur = s_list.begin(); cur != s_list.end(); ++cur)
+ {
+ std::cout << " " << cur->order << ":" << std::endl;
+
+ std::cout << " z_row: ";
+ for (typename ZRow::const_iterator zcur = cur->z_row.begin(); zcur != cur->z_row.end(); ++zcur)
+ std::cout << (*zcur)->order << " ";
+ std::cout << std::endl;
+
+ std::cout << " c_row: ";
+ for (typename CRow::const_iterator ccur = cur->c_row.begin(); ccur != cur->c_row.end(); ++ccur)
+ std::cout << (*ccur)->order << " ";
+ std::cout << std::endl;
+
+ std::cout << " low: ";
+ if (cur->low != z_list.end())
+ std::cout << cur->low->order;
+ else
+ std::cout << "none";
+ std::cout << std::endl;
+ }
+
+ std::cout << "z_list:" << std::endl;
+ for (ZIndex cur = z_list.begin(); cur != z_list.end(); ++cur)
+ {
+ std::cout << " " << cur->order << ":" << std::endl;
+
+ std::cout << " birth: " << cur->birth << std::endl;
+
+ std::cout << " z_column: ";
+ for (typename ZColumn::const_iterator zcur = cur->z_column.begin(); zcur != cur->z_column.end(); ++zcur)
+ std::cout << (*zcur)->order << " ";
+ std::cout << std::endl;
+
+ std::cout << " b_row: ";
+ for (typename BRow::const_iterator bcur = cur->b_row.begin(); bcur != cur->b_row.end(); ++bcur)
+ std::cout << (*bcur)->order << " ";
+ std::cout << std::endl;
+
+ std::cout << " low: ";
+ if (cur->low != b_list.end())
+ std::cout << cur->low->order;
+ else
+ std::cout << "none";
+ std::cout << std::endl;
+ }
+
+ std::cout << "b_list:" << std::endl;
+ for (BIndex cur = b_list.begin(); cur != b_list.end(); ++cur)
+ {
+ std::cout << " " << cur->order << ":" << std::endl;
+
+ std::cout << " b_column: ";
+ for (typename BColumn::const_iterator bcur = cur->b_column.begin(); bcur != cur->b_column.end(); ++bcur)
+ std::cout << (*bcur)->order << " ";
+ std::cout << std::endl;
+
+ std::cout << " c_column: ";
+ for (typename CColumn::const_iterator ccur = cur->c_column.begin(); ccur != cur->c_column.end(); ++ccur)
+ std::cout << (*ccur)->order << " ";
+ std::cout << std::endl;
+ }
+}
+
+template<class BID, class SD>
+bool
+ZigzagPersistence<BID,SD>::
+check_consistency(SimplexIndex, ZIndex, BIndex)
+{
+#ifdef ZIGZAG_CONSISTENCY
+ #warning "Checking consistency in ZigzagPersistence"
+
+ Count(cZigzagConsistency);
+ for (SimplexIndex cur = s_list.begin(); cur != s_list.end(); ++cur)
+ {
+ if (cur == s_skip) continue;
+ //rLog(rlZigzagCheckConsistency, "SimplexIndex cur: %d", cur->order);
+ for (typename ZRow::const_iterator zcur = cur->z_row.begin(); zcur != cur->z_row.end(); ++zcur)
+ if (std::find((*zcur)->z_column.begin(), (*zcur)->z_column.end(), cur) == (*zcur)->z_column.end())
+ {
+ rError("In check_consistency(): SimplexNode %d not found in z_column of %d", cur->order, (*zcur)->order);
+ return false;
+ }
+ for (typename CRow::const_iterator ccur = cur->c_row.begin(); ccur != cur->c_row.end(); ++ccur)
+ if (std::find((*ccur)->c_column.begin(), (*ccur)->c_column.end(), cur) == (*ccur)->c_column.end())
+ {
+ rError("In check_consistency(): SimplexNode %d not found in c_column of %d", cur->order, (*ccur)->order);
+ return false;
+ }
+ if (cur->low != z_list.end())
+ AssertMsg(!(cur->low->z_column.empty()), "z_column must not be empty");
+ if (cur->low != z_list.end() && cur->low->z_column.back() != cur)
+ {
+ rError("low of SimplexNode %d is incorrect", cur->order);
+ return false;
+ }
+ }
+
+ for (ZIndex cur = z_list.begin(); cur != z_list.end(); ++cur)
+ {
+ if (cur == z_skip) continue;
+
+ //rLog(rlZigzagCheckConsistency, "ZIndex cur: %d", cur->order);
+ for (typename ZColumn::const_iterator scur = cur->z_column.begin(); scur != cur->z_column.end(); ++scur)
+ if (std::find((*scur)->z_row.begin(), (*scur)->z_row.end(), cur) == (*scur)->z_row.end())
+ {
+ rError("In check_consistency(): ZNode %d not found in z_row of %d", cur->order, (*scur)->order);
+ return false;
+ }
+ for (typename BRow::const_iterator bcur = cur->b_row.begin(); bcur != cur->b_row.end(); ++bcur)
+ if (std::find((*bcur)->b_column.begin(), (*bcur)->b_column.end(), cur) == (*bcur)->b_column.end())
+ {
+ rError("In check_consistency(): ZNode %d not found in b_column of %d", cur->order, (*bcur)->order);
+ return false;
+ }
+ if (cur->low != b_list.end() && cur->low->b_column.back() != cur)
+ {
+ rError("low of ZNode %d is incorrect", cur->order);
+ return false;
+ }
+ if (cur->z_column.back()->low != cur)
+ {
+ rError("The low of the back of the z_column must be set correctly");
+ rError(" %d [%s], its back %d with low=%d", cur->order,
+ cur->z_column.tostring(out).c_str(),
+ cur->z_column.back()->order,
+ (cur->z_column.back()->low == z_list.end()) ? 0 : cur->z_column.back()->low->order);
+ return false;
+ }
+ }
+
+ for (BIndex cur = b_list.begin(); cur != b_list.end(); ++cur)
+ {
+ if (cur == b_skip) continue;
+
+ //rLog(rlZigzagCheckConsistency, "BIndex cur: %d", cur->order);
+ for (typename BColumn::const_iterator zcur = cur->b_column.begin(); zcur != cur->b_column.end(); ++zcur)
+ if (std::find((*zcur)->b_row.begin(), (*zcur)->b_row.end(), cur) == (*zcur)->b_row.end())
+ {
+ rError("In check_consistency(): BNode %d not found in b_row of %d", cur->order, (*zcur)->order);
+ return false;
+ }
+ for (typename CColumn::const_iterator scur = cur->c_column.begin(); scur != cur->c_column.end(); ++scur)
+ if (std::find((*scur)->c_row.begin(), (*scur)->c_row.end(), cur) == (*scur)->c_row.end())
+ {
+ rError("In check_consistency(): BNode %d not found in c_row of %d", cur->order, (*scur)->order);
+ return false;
+ }
+ if (!(cur->b_column.empty() || cur->b_column.back()->low == cur))
+ {
+ rError("The low of the back of the b_column must be set correctly");
+ return false;
+ }
+
+ // ZB == DC
+ ZColumn zb, dc;
+ std::for_each(cur->b_column.begin(), cur->b_column.end(), make_adder(&ZNode::z_column, zb));
+ std::for_each(cur->c_column.begin(), cur->c_column.end(), make_adder(&SimplexNode::boundary, dc));
+ zb.add(dc, cmp);
+ if (!zb.empty())
+ {
+ rError(" b_column: [%s]", cur->b_column.tostring(out).c_str());
+ rError(" c_column: [%s]", cur->c_column.tostring(out).c_str());
+ rError(" zb - dc: [%s]", zb.tostring(out).c_str());
+ rError("ZB = DC");
+ return false;
+ }
+ }
+#endif
+
+ return true;
+}
+
+/* Private */
+
+// Class: Appender
+//
+// Functor that appends given element to the given member of whatever parameter it is invoked with
+template<class BID, class SD>
+template<class Member, class Element>
+struct ZigzagPersistence<BID,SD>::Appender
+{
+ Appender(Member mm, Element ee):
+ m(mm), e(ee) {}
+
+ template<class T>
+ void operator()(T& a) { ((*a).*m).append(e, cmp); }
+
+ Member m;
+ Element e;
+ OrderComparison cmp;
+};
+
+// Class: Remover
+//
+// Functor that removes given element from the given member of whatever parameter it is invoked with
+template<class BID, class SD>
+template<class Member, class Element>
+struct ZigzagPersistence<BID,SD>::Remover
+{
+ Remover(Member mm, Element ee):
+ m(mm), e(ee) {}
+
+ template<class T>
+ void operator()(T& a) { ((*a).*m).remove(e); }
+
+ Member m;
+ Element e;
+};
+
+// Class: Adder
+//
+// Functor that adds the given member of whatever it is invoked with to the given chain
+template<class BID, class SD>
+template<class Member, class Chain>
+struct ZigzagPersistence<BID,SD>::Adder
+{
+ Adder(Member mm, Chain& cc):
+ m(mm), c(cc) {}
+
+ template<class T>
+ void operator()(T& a) { c.add((*a).*m, cmp); }
+
+ Member m;
+ Chain& c;
+ OrderComparison cmp;
+};
+
+
+// Function: add_chains()
+//
+// Special case of add_chains where all Indexes are the same, and
+// therefore PrimaryMemberFrom and PrimaryMemberTo are the same
+template<class BID, class SD>
+template<class Index, class IndexFrom, class PrimaryMember, class SecondaryMember>
+void
+ZigzagPersistence<BID,SD>::
+add_chains(Index bg, Index end, IndexFrom j, PrimaryMember pm, SecondaryMember sm)
+{
+ add_chains(bg, end, j, pm, sm, pm);
+}
+
+// Function: add_chains()
+//
+// Adds PrimaryMember pm of j to pm of every element in the range [bg,end)
+// Fixes SecondaryMembers by adding and removing the corresponding elements.
+// For example, if we add a column to a number of other columns, then PrimaryMember is that
+// column member, and SecondaryMember is the corresponding row member.
+template<class BID, class SD>
+template<class IndexTo, class IndexFrom, class PrimaryMemberTo, class SecondaryMemberTo, class PrimaryMemberFrom>
+void
+ZigzagPersistence<BID,SD>::
+add_chains(IndexTo bg, IndexTo end, IndexFrom j, PrimaryMemberTo pmt, SecondaryMemberTo smt, PrimaryMemberFrom pmf)
+{
+ for (IndexTo cur = bg; cur != end; ++cur)
+ add_chain(*cur, j, pmt, smt, pmf);
+}
+
+// Function: change_basis()
+//
+// Changes basis by adding PrimaryMember pm of j to pm of every element in range [bg, end).
+// In parallel it performs the reverse (complementary) update on the dual members, i.e.
+// column and row operations are performed in sync, so that the product of the two matrices doesn't change
+template<class BID, class SD>
+template<class IndexTo, class IndexFrom, class PrimaryMember, class SecondaryMember, class DualPrimaryMember, class DualSecondaryMember>
+void
+ZigzagPersistence<BID,SD>::
+change_basis(IndexTo bg, IndexTo end, IndexFrom j, PrimaryMember pm, SecondaryMember sm, DualPrimaryMember dpm, DualSecondaryMember dsm)
+{
+ for (IndexTo cur = bg; cur != end; ++cur)
+ {
+ add_chain(*cur, j, pm, sm, pm);
+ add_chain(j, *cur, dpm, dsm, dpm);
+ }
+}
+
+template<class BID, class SD>
+template<class Index, class PrimaryMember, class SecondaryMember>
+void
+ZigzagPersistence<BID,SD>::
+add_chain(Index to, Index from, PrimaryMember pm, SecondaryMember sm)
+{
+ add_chain(to, from, pm, sm, pm);
+}
+
+// Function: add_chain()
+//
+// Adds PrimaryMemberFrom pmf of `from` to PrimaryMemberTo pmt of `to`.
+// Fixes SecondaryMemberTos. See add_chains().
+template<class BID, class SD>
+template<class IndexTo, class IndexFrom, class PrimaryMemberTo, class SecondaryMemberTo, class PrimaryMemberFrom>
+void
+ZigzagPersistence<BID,SD>::
+add_chain(IndexTo to, IndexFrom from, PrimaryMemberTo pmt, SecondaryMemberTo smt, PrimaryMemberFrom pmf)
+{
+ rLog(rlZigzagAddChain, "Adding %d [%s] to %d [%s]",
+ (*from).order,
+ ((*from).*pmf).tostring(out).c_str(),
+ (*to).order,
+ ((*to).*pmt).tostring(out).c_str());
+
+ // Fix secondaries
+ std::for_each(make_intersection_iterator(((*from).*pmf).begin(), ((*from).*pmf).end(),
+ ((*to).*pmt).begin(), ((*to).*pmt).end(),
+ cmp),
+ make_intersection_iterator(((*from).*pmf).end(), ((*from).*pmf).end(),
+ ((*to).*pmt).end(), ((*to).*pmt).end(),
+ cmp),
+ make_remover(smt, to));
+ std::for_each(make_difference_iterator(((*from).*pmf).begin(), ((*from).*pmf).end(),
+ ((*to).*pmt).begin(), ((*to).*pmt).end(),
+ cmp),
+ make_difference_iterator(((*from).*pmf).end(), ((*from).*pmf).end(),
+ ((*to).*pmt).end(), ((*to).*pmt).end(),
+ cmp),
+ make_appender(smt, to));
+
+ // Add primaries
+ ((*to).*pmt).add((*from).*pmf, cmp);
+ rLog(rlZigzagAddChain, "Got %s", ((*to).*pmt).tostring(out).c_str());
+}
+
+
+/* ZigzagVisitor */
+template<class BID, class SD>
+typename ZigzagPersistence<BID,SD>::SimplexIndex
+ZigzagPersistence<BID,SD>::ZigzagVisitor::
+new_simplex(ZigzagPersistence& zz)
+{
+ unsigned order = zz.s_list.empty() ? 0 : boost::prior(zz.s_list.end())->order + 1;
+ zz.s_list.push_back(SimplexNode(order, zz.z_list.end()));
+ return boost::prior(zz.s_list.end());
+}
+
+template<class BID, class SD>
+typename ZigzagPersistence<BID,SD>::ZIndex
+ZigzagPersistence<BID,SD>::ZigzagVisitor::
+new_z_in_add(ZigzagPersistence& zz, const ZColumn&, const BRow&)
+{
+ int order = zz.z_list.empty() ? 0 : boost::prior(zz.z_list.end())->order + 1;
+ zz.z_list.push_back(ZNode(order, zz.b_list.end()));
+ return boost::prior(zz.z_list.end());
+}
+
+template<class BID, class SD>
+typename ZigzagPersistence<BID,SD>::BIndex
+ZigzagPersistence<BID,SD>::ZigzagVisitor::
+select_j_in_remove(ZigzagPersistence&, const CRow& c_row)
+{
+ return c_row.front();
+}
+
+template<class BID, class SD>
+typename ZigzagPersistence<BID,SD>::ZIndex
+ZigzagPersistence<BID,SD>::ZigzagVisitor::
+new_z_in_remove(ZigzagPersistence& zz)
+{
+ int order = zz.z_list.empty() ? 0 : zz.z_list.begin()->order - 1;
+ zz.z_list.push_front(ZNode(order, zz.b_list.end()));
+ return zz.z_list.begin();
+}
+
+template<class BID, class SD>
+typename ZigzagPersistence<BID,SD>::Death
+ZigzagPersistence<BID,SD>::ZigzagVisitor::
+death(ZigzagPersistence&, ZIndex dying_z)
+{
+ return Death(dying_z->birth);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/binaryheap.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,302 @@
+// Heap Plus implementation 1.01alpha
+// ChangeLog:
+// 2009.09.26 - Updated __down_heap, added _updatepos (Yanbin Lu)
+// 2009.08.10 - Added update_heap_pos functions so that we can adjust
+// heap values outside of update_heap functions -- e.g., if
+// we have external pointers into the heap entries -- then
+// call update_heap on the position only, regardless of the value.
+// (Danny Tarlow: dtarlow@cs.toronto.edu)
+// 2006.12.18 - Initially created (lihw)
+
+#ifndef HEAPPLUS_H_
+#define HEAPPLUS_H_
+
+#include <iterator>
+
+namespace std {
+ template<typename _RandomAccessIterator, typename _Distance, typename _Tp,
+ typename _Compare, typename _Updatepos>
+ void
+ __push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
+ _Distance __topIndex, _Tp __value, _Compare __comp, _Updatepos __updatepos)
+ {
+ _Distance __parent = (__holeIndex - 1) / 2;
+ while (__holeIndex > __topIndex
+ && __comp(*(__first + __parent), __value))
+ {
+ *(__first + __holeIndex) = *(__first + __parent);
+ __updatepos(*(__first + __holeIndex), __holeIndex); /* yanbin */
+ __holeIndex = __parent;
+ __parent = (__holeIndex - 1) / 2;
+ }
+ *(__first + __holeIndex) = __value;
+ __updatepos(*(__first + __holeIndex), __holeIndex); /* yanbin */
+ }
+
+ template<typename _RandomAccessIterator, typename _Compare, typename _Updatepos>
+ inline void
+ push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _Compare __comp, _Updatepos __updatepos)
+ {
+ typedef typename iterator_traits<_RandomAccessIterator>::value_type
+ _ValueType;
+ typedef typename iterator_traits<_RandomAccessIterator>::difference_type
+ _DistanceType;
+
+ // concept requirements
+ //__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
+ // _RandomAccessIterator>)
+ //__glibcxx_requires_valid_range(__first, __last);
+ //__glibcxx_requires_heap_pred(__first, __last - 1, __comp);
+
+ std::__push_heap(__first, _DistanceType((__last - __first) - 1),
+ _DistanceType(0), _ValueType(*(__last - 1)), __comp, __updatepos);
+ }
+
+ template<typename _RandomAccessIterator, typename _Distance,
+ typename _Tp, typename _Compare, typename _Updatepos>
+ void
+ __adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
+ _Distance __len, _Tp __value, _Compare __comp, _Updatepos __updatepos)
+ {
+ const _Distance __topIndex = __holeIndex;
+ _Distance __secondChild = 2 * __holeIndex + 2;
+ while (__secondChild < __len)
+ {
+ if (__comp(*(__first + __secondChild),
+ *(__first + (__secondChild - 1))))
+ __secondChild--;
+ *(__first + __holeIndex) = *(__first + __secondChild);
+ __updatepos(*(__first + __holeIndex), __holeIndex); /* yanbin */
+ __holeIndex = __secondChild;
+ __secondChild = 2 * (__secondChild + 1);
+ }
+ if (__secondChild == __len)
+ {
+ *(__first + __holeIndex) = *(__first + (__secondChild - 1));
+ __updatepos(*(__first + __holeIndex), __holeIndex); /* yanbin */
+ __holeIndex = __secondChild - 1;
+ }
+ std::__push_heap(__first, __holeIndex, __topIndex, __value, __comp, __updatepos);
+ }
+
+ template<typename _RandomAccessIterator, typename _Tp, typename _Compare, typename _Updatepos>
+ inline void
+ __pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __result, _Tp __value, _Compare __comp, _Updatepos __updatepos)
+ {
+ typedef typename iterator_traits<_RandomAccessIterator>::difference_type
+ _Distance;
+ *__result = *__first;
+ std::__adjust_heap(__first, _Distance(0), _Distance(__last - __first),
+ __value, __comp, __updatepos);
+ }
+
+ template<typename _RandomAccessIterator, typename _Compare, typename _Updatepos>
+ inline void
+ pop_heap(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Compare __comp, _Updatepos __updatepos)
+ {
+ // concept requirements
+ //__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
+ // _RandomAccessIterator>)
+ //__glibcxx_requires_valid_range(__first, __last);
+ //__glibcxx_requires_heap_pred(__first, __last, __comp);
+
+ typedef typename iterator_traits<_RandomAccessIterator>::value_type
+ _ValueType;
+ std::__pop_heap(__first, __last - 1, __last - 1,
+ _ValueType(*(__last - 1)), __comp, __updatepos);
+ }
+
+ template<typename _RandomAccessIterator, typename _Compare, typename _Updatepos>
+ inline void
+ make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _Compare __comp, _Updatepos __updatepos)
+ {
+ typedef typename iterator_traits<_RandomAccessIterator>::value_type
+ _ValueType;
+ typedef typename iterator_traits<_RandomAccessIterator>::difference_type
+ _DistanceType;
+
+ // concept requirements
+ //__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
+ // _RandomAccessIterator>)
+ // __glibcxx_requires_valid_range(__first, __last);
+
+ if (__last - __first < 2)
+ {
+ for (_DistanceType __len = 0; __len < __last - __first; __len ++)
+ {
+ __updatepos(*(__first + __len), __len); /* yanbin */
+ }
+
+ return;
+ }
+ const _DistanceType __len = __last - __first;
+ _DistanceType __parent = (__len - 2) / 2;
+ while (true)
+ {
+ std::__adjust_heap(__first, __parent, __len,
+ _ValueType(*(__first + __parent)), __comp, __updatepos);
+ if (__parent == 0)
+ {
+ for (_DistanceType __len = 0; __len < __last - __first; __len ++)
+ {
+ __updatepos(*(__first + __len), __len); /* yanbin */
+ }
+
+ return;
+ }
+ __parent--;
+ }
+
+ }
+
+
+ template<typename _RandomAccessIterator, typename _Distance, typename _Tp, typename _Compare, typename _Updatepos>
+inline void
+__up_heap(_RandomAccessIterator __first, _RandomAccessIterator __end, _RandomAccessIterator __pos,
+ _Distance, _Tp __value, _Compare __comp, _Updatepos __updatepos)
+{
+ _Distance __parent = (__pos - __first - 1) / 2;
+ _Distance __index = __pos - __first;
+ while (__index > 0 && __comp(*(__first + __parent), __value)) {
+ *(__first + __index) = *(__first + __parent);
+ __updatepos(*(__first + __index), __index); /* yanbin */
+
+ __index = __parent;
+ __parent = (__parent - 1) / 2;
+ }
+
+ if (__pos != (__first + __index)) {
+ *(__first + __index) = __value;
+ __updatepos(*(__first + __index), __index); /* yanbin */
+ }
+}
+
+template<typename _RandomAccessIterator, typename _Distance, typename _Tp, typename _Compare, typename _Updatepos>
+inline void
+__down_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _RandomAccessIterator __pos,
+ _Distance, _Tp __value, _Compare __comp, _Updatepos __updatepos)
+{
+ _Distance __len = __last - __first;
+ _Distance __index = __pos - __first;
+ _Distance __left = __index * 2 + 1;
+ _Distance __right = __index * 2 + 2;
+ _Distance __largest;
+ while (__index < __len)
+ {
+ if (__right < __len)
+ {
+ if (__comp(*(__first + __left), *(__first + __right)))
+ {
+ __largest = __right;
+ }
+ else
+ {
+ __largest = __left;
+ }
+ }
+ else if (__left < __len)
+ {
+ __largest = __left;
+ }
+ else
+ {
+ __largest = __index;
+ }
+
+ if (__largest < __len && __comp(__value, *(__first + __largest)))
+ {
+ *(__first + __index) = *(__first + __largest);
+ __updatepos(*(__first + __index), __index); /* yanbin */
+ __index = __largest;
+ __left = __index * 2 + 1;
+ __right = __index * 2 + 2;
+ } else
+ break;
+ }
+
+ if (__pos != (__first + __index)) {
+ *(__first + __index) = __value;
+ __updatepos(*(__first + __index), __index); /* yanbin */
+ }
+}
+
+template<typename _RandomAccessIterator, typename _Distance, typename _Tp,
+ typename _Compare, typename _Updatepos>
+inline void
+__update_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __pos, _Distance, _Tp __value, _Compare __comp, _Updatepos __updatepos)
+{
+ *(__pos) = __value;
+
+ _Distance __index = (__pos - __first);
+ _Distance __parent = (__index - 1) / 2;
+
+ if (__index > 0 && __comp(*(__first + __parent), __value))
+ __up_heap(__first, __last, __pos, _Distance(), __value, __comp, __updatepos);
+ else
+ __down_heap(__first, __last, __pos, _Distance(), __value, __comp, __updatepos);
+}
+
+template<typename _RandomAccessIterator, typename _Distance, typename _Compare, typename _Updatepos>
+inline void
+__update_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __pos, _Distance, _Compare __comp, _Updatepos __updatepos)
+{
+ _Distance __index = (__pos - __first);
+ _Distance __parent = (__index - 1) / 2;
+ if (__index > 0 && __comp(*(__first + __parent), *(__pos)))
+ __up_heap(__first, __last, __pos, _Distance(), *(__pos), __comp, __updatepos);
+ else
+ __down_heap(__first, __last, __pos, _Distance(), *(__pos), __comp, __updatepos);
+}
+
+template<typename _RandomAccessIterator, typename _Tp, typename _Updatepos>
+inline void
+update_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __pos, _Tp __value, _Updatepos __updatepos)
+{
+ typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;
+ typedef typename iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;
+
+ __update_heap(__first, __last, __pos, _DistanceType(), __value, less<_ValueType>(), __updatepos);
+}
+
+template<typename _RandomAccessIterator, typename _Tp, typename _Compare, typename _Updatepos>
+inline void
+update_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __pos, _Tp __value, _Compare __comp, _Updatepos __updatepos)
+{
+ __update_heap(__first, __last, __pos, __value, __comp, __updatepos);
+}
+
+
+ template<typename _RandomAccessIterator, typename _Updatepos>
+inline void
+update_heap_pos(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __pos, _Updatepos __updatepos) {
+ typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;
+ typedef typename iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;
+
+ __update_heap(__first, __last, __pos, _DistanceType(), less<_ValueType>(), __updatepos);
+}
+
+
+template<typename _RandomAccessIterator, typename _Compare, typename _Updatepos>
+inline void
+update_heap_pos(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __pos, _Compare __comp, _Updatepos __updatepos) {
+ typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;
+ typedef typename iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;
+
+ __update_heap(__first, __last, __pos, _DistanceType(), __comp, __updatepos);
+}
+
+
+
+}; // namespace std
+
+#endif // !HEAPPLUS_H_
--- a/include/utilities/consistencylist.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/utilities/consistencylist.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,5 +1,5 @@
/*
- * Author: Dmitriy Morozov
+ * Author: Dmitriy Morozov
* Department of Computer Science, Duke University, 2006
*/
@@ -31,7 +31,7 @@
class ConsistencyList: public OrderList<ConsistencyListNode<T> >
{
public:
- class OrderComparison;
+ class OrderComparison;
class LessThanComparison;
class GreaterThanComparison;
class ConsistencyComparison;
@@ -43,20 +43,20 @@
typedef GreaterThanComparison GreaterThanComparison;
typedef ConsistencyComparison ConsistencyComparison;
/// @}
-
+
typedef ConsistencyListNode<T> NodeType;
typedef ConsistencyList<T> Self;
typedef OrderList<NodeType > Parent;
-
+
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef ConsistencyListIterator<T> iterator;
typedef const_ConsistencyListIterator<T> const_iterator;
-
+
ConsistencyList(): sz(0) {}
~ConsistencyList() { clear(); }
-
+
/// \name Order operations
void swap(iterator i, iterator j); ///< Exchanges the order of simplices pointed to by i and j
template<class BinaryPredicate>
@@ -69,7 +69,7 @@
iterator insert(iterator predecessor, const_reference x); ///< Inserts x immediately after predecessor (has to be a valid iterator)
void erase(iterator x) { Parent::erase(x.get_base()); --sz; }
- void clear() { return Parent::clear(); }
+ void clear() { return Parent::clear(); }
bool empty() const { return Parent::empty(); }
SizeType size() const { return sz; }
iterator begin() { return iterator(Parent::begin()); }
@@ -79,11 +79,11 @@
reference back() { return Parent::back(); }
const_reference back() const { return Parent::back(); }
void pop_back() { return Parent::pop_back(); }
-
+
iterator last() { return iterator(boost::prior(end())); }
const_iterator last() const { return const_iterator(boost::prior(end())); }
/// @}
-
+
private:
unsigned int sz;
};
@@ -93,31 +93,31 @@
class ConsistencyList<T>::OrderComparison
{
public:
- typedef typename ConsistencyList<T>::const_iterator ComparableType;
+ typedef typename ConsistencyList<T>::const_iterator ComparableType;
- int compare(ComparableType a, ComparableType b) const; /// (-1,0,1) = a (precedes, ==, succeeds) b
+ int compare(ComparableType a, ComparableType b) const; /// (-1,0,1) = a (precedes, ==, succeeds) b
};
/// Determines if the first element is less than the second one
template<class T>
-class ConsistencyList<T>::LessThanComparison: public OrderComparison
+class ConsistencyList<T>::LessThanComparison: public OrderComparison
{
public:
typedef OrderComparison Parent;
- typedef typename Parent::ComparableType ComparableType;
-
- int compare(ComparableType a, ComparableType b) const;
+ typedef typename Parent::ComparableType ComparableType;
+
+ int compare(ComparableType a, ComparableType b) const;
bool operator()(ComparableType a, ComparableType b) const;
};
/// Determines if the first element is greater than the second one
template<class T>
-class ConsistencyList<T>::GreaterThanComparison: public OrderComparison
+class ConsistencyList<T>::GreaterThanComparison: public OrderComparison
{
public:
typedef OrderComparison Parent;
- typedef typename Parent::ComparableType ComparableType;
-
+ typedef typename Parent::ComparableType ComparableType;
+
int compare(ComparableType a, ComparableType b) const;
bool operator()(ComparableType a, ComparableType b) const;
};
@@ -127,10 +127,10 @@
class ConsistencyList<T>::ConsistencyComparison
{
public:
- typedef typename ConsistencyList<T>::const_iterator ComparableType;
-
- int compare(ComparableType a, ComparableType b) const; ///< (-1,0,1) = a (precedes, ==, succeeds) b
- bool operator()(ComparableType a, ComparableType b) const;
+ typedef typename ConsistencyList<T>::const_iterator ComparableType;
+
+ int compare(ComparableType a, ComparableType b) const; ///< (-1,0,1) = a (precedes, ==, succeeds) b
+ bool operator()(ComparableType a, ComparableType b) const;
};
/// Structure storing auxilliary information requred for each node of ConsistencyList
@@ -140,11 +140,11 @@
ConsistencyListNode(const T& d, unsigned int c):
data(d), consistency(c)
{}
-
+
T data;
OrderType consistency;
- std::ostream& operator<<(std::ostream& out) const { return out << data << ": " << consistency; }
+ std::ostream& operator<<(std::ostream& out) const { return out << consistency << ": " << data; }
};
template<class T>
@@ -157,7 +157,7 @@
{
private:
struct enabler {};
-
+
public:
typedef typename ConsistencyList<T>::Parent ConsistencyListParent;
typedef boost::iterator_adaptor<ConsistencyListIterator<T>,
@@ -168,10 +168,10 @@
ConsistencyListIterator() {}
ConsistencyListIterator(const typename ConsistencyListParent::iterator& iter):
- ConsistencyListIterator::iterator_adaptor_(iter)
+ ConsistencyListIterator::iterator_adaptor_(iter)
{}
ConsistencyListIterator(const ConsistencyListIterator<T>& other):
- ConsistencyListIterator::iterator_adaptor_(other.base())
+ ConsistencyListIterator::iterator_adaptor_(other.base())
{}
private:
@@ -189,7 +189,7 @@
{
private:
struct enabler {};
-
+
public:
typedef typename ConsistencyList<T>::Parent ConsistencyListParent;
typedef boost::iterator_adaptor<const_ConsistencyListIterator<T>,
@@ -200,13 +200,13 @@
const_ConsistencyListIterator() {}
const_ConsistencyListIterator(const typename ConsistencyListParent::const_iterator& iter):
- const_ConsistencyListIterator::iterator_adaptor_(iter)
+ const_ConsistencyListIterator::iterator_adaptor_(iter)
{}
const_ConsistencyListIterator(const const_ConsistencyListIterator<T>& other):
- const_ConsistencyListIterator::iterator_adaptor_(other.base())
+ const_ConsistencyListIterator::iterator_adaptor_(other.base())
{}
const_ConsistencyListIterator(const ConsistencyListIterator<T>& other):
- const_ConsistencyListIterator::iterator_adaptor_(other.base())
+ const_ConsistencyListIterator::iterator_adaptor_(other.base())
{}
private:
@@ -216,7 +216,6 @@
get_base() { return Parent::base_reference(); }
friend class ConsistencyList<T>::OrderComparison;
- friend class ConsistencyList<T>::ConsistencyComparison;
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/containers.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,228 @@
+#ifndef __CONTAINERS_H__
+#define __CONTAINERS_H__
+
+#include "circular_list.h"
+
+#if DEBUG_CONTAINERS
+ #include <debug/vector>
+ #include <debug/deque>
+ using std::__debug::vector;
+ using std::__debug::deque;
+#else
+ #include <vector>
+ #include <deque>
+ using std::vector;
+ using std::deque;
+#endif
+
+// TODO: write documentation
+
+template<class Container_, class Comparison_ = std::less<typename Container_::value_type> >
+struct ContainerTraits
+{
+ typedef Container_ Container;
+ typedef typename Container::value_type Item;
+ typedef typename Container::const_reference const_reference;
+ typedef Comparison_ Comparison;
+
+ static void reserve(Container& c, size_t sz) {}
+ static void sort(Container& c, const Comparison& cmp = Comparison()) { c.sort(cmp); }
+ static void push_front(Container& c, const_reference x) { c.push_front(x); }
+};
+
+/**
+ * Class: SizeStorage
+ *
+ * This class expresses how size should be stored for various containers to be able
+ * to deduce it in constant time. By default it is stored explicitly, so that if the
+ * Container is std::list everything works. However, specialization is available for
+ * std::vector which uses its builtin size() function.
+ */
+template<class Container_>
+class SizeStorage
+{
+ public:
+ typedef Container_ Container;
+ typedef SizeStorage<Container> Self;
+
+ SizeStorage(size_t size = 0): size_(size) {}
+
+ Self& operator+=(size_t inc) { size_ += inc; return *this; }
+ Self& operator-=(size_t dec) { size_ -= dec; return *this; }
+ Self& operator++() { ++size_; return *this; }
+ Self operator++(int) { Self tmp = *this; size_++; return tmp; }
+ Self& operator--() { --size_; return *this; }
+ Self operator--(int) { Self tmp = *this; size_--; return tmp; }
+ size_t size(const Container& c) const { return size_; }
+ void swap(SizeStorage& other) { std::swap(size_, other.size_); }
+
+ void clear() { size_ = 0; }
+
+ private:
+ size_t size_;
+};
+
+/**
+ * Class: CountingBackInserter<C>
+ *
+ * Derives from std::back_insert_iterator<C> and SizeStorage<C>,
+ * and keeps track of the number of inserted elements.
+ */
+template<class C>
+struct CountingBackInserter: public std::back_insert_iterator<C>,
+ public SizeStorage<C>
+{
+ typedef CountingBackInserter Self;
+ typedef std::back_insert_iterator<C> ParentIterator;
+ typedef SizeStorage<C> ParentSize;
+
+ CountingBackInserter(C& c):
+ ParentIterator(c) {}
+
+ Self& operator++() { ParentSize::operator++(); ParentIterator::operator++(); return *this; }
+ Self operator++(int i) { Self tmp = *this; ParentSize::operator++(i); ParentIterator::operator++(i); return tmp; }
+};
+
+/**
+ * Class: PushBackFunctor<Container>
+ *
+ * Performs the same task as std::back_insert_iterator<Container>, but as a functor.
+ */
+template<class Container_>
+class PushBackFunctor
+{
+ public:
+ typedef Container_ Container;
+ typedef typename Container::value_type value_type;
+
+ PushBackFunctor(Container& container):
+ container_(container) {}
+
+ void operator()(const value_type& v) const { container_.push_back(v); }
+
+ private:
+ Container& container_;
+};
+
+template<class Container>
+PushBackFunctor<Container>
+make_push_back_functor(Container& container) { return PushBackFunctor<Container>(container); }
+
+/**
+ * Class: InsertFunctor<Container>
+ *
+ * Performs insertions of its arguments into the given container.
+ */
+template<class Container_>
+class InsertFunctor
+{
+ public:
+ typedef Container_ Container;
+ typedef typename Container::value_type value_type;
+
+ InsertFunctor(Container& container):
+ container_(container) {}
+
+ void operator()(const value_type& v) const { container_.insert(v); }
+
+ private:
+ Container& container_;
+};
+
+template<class Container>
+InsertFunctor<Container>
+make_insert_functor(Container& container) { return InsertFunctor<Container>(container); }
+
+/* Specializations */
+
+template<class T, class Comparison_>
+struct ContainerTraits<vector<T>, Comparison_>
+{
+ typedef T Item;
+ typedef vector<T> Container;
+ typedef typename Container::const_reference const_reference;
+ typedef Comparison_ Comparison;
+
+ static void reserve(Container& c, size_t sz) { c.reserve(sz); }
+ static void sort(Container& c, const Comparison& cmp = Comparison()) { std::sort(c.begin(), c.end(), cmp); }
+ static void push_front(Container& c, const_reference x) { c.insert(c.begin(), x); }
+};
+
+template<class T, class Comparison_>
+struct ContainerTraits<deque<T>, Comparison_>
+{
+ typedef T Item;
+ typedef deque<T> Container;
+ typedef typename Container::const_reference const_reference;
+ typedef Comparison_ Comparison;
+
+ static void reserve(Container& c, size_t sz) { }
+ static void sort(Container& c, const Comparison& cmp = Comparison()) { std::sort(c.begin(), c.end(), cmp); }
+ static void push_front(Container& c, const_reference x) { c.push_front(x); }
+};
+
+template<class T, class Comparison_>
+struct ContainerTraits<List<T>, Comparison_>
+{
+ typedef T Item;
+ typedef List<T> Container;
+ typedef typename Container::const_reference const_reference;
+ typedef Comparison_ Comparison;
+
+ static void reserve(Container& c, size_t sz) { }
+ static void sort(Container& c, const Comparison& cmp = Comparison())
+ {
+ vector<Item> tmp(c.begin(), c.end());
+ std::sort(tmp.begin(), tmp.end(), cmp);
+ std::copy(tmp.begin(), tmp.end(), c.begin());
+ }
+ static void push_front(Container& c, const_reference x) { c.push_front(x); }
+};
+
+// TODO: specialize for List (singly-linked list)
+
+template<class T>
+class SizeStorage<vector<T> >
+{
+ public:
+ typedef vector<T> Container;
+ typedef SizeStorage<Container> Self;
+
+ SizeStorage() {}
+ SizeStorage(size_t) {}
+
+ Self& operator+=(size_t) { return *this; }
+ Self& operator-=(size_t) { return *this; }
+ Self& operator++() { return *this; }
+ Self operator++(int) { return *this; }
+ Self& operator--() { return *this; }
+ Self operator--(int) { return *this; }
+ size_t size(const Container& c) const { return c.size(); }
+ void swap(SizeStorage&) {}
+
+ void clear() {}
+};
+
+template<class T>
+class SizeStorage<deque<T> >
+{
+ public:
+ typedef deque<T> Container;
+ typedef SizeStorage<Container> Self;
+
+ SizeStorage() {}
+ SizeStorage(size_t) {}
+
+ Self& operator+=(size_t) { return *this; }
+ Self& operator-=(size_t) { return *this; }
+ Self& operator++() { return *this; }
+ Self operator++(int) { return *this; }
+ Self& operator--() { return *this; }
+ Self operator--(int) { return *this; }
+ size_t size(const Container& c) const { return c.size(); }
+ void swap(SizeStorage&) {}
+
+ void clear() {}
+};
+
+#endif // __CONTAINERS_H__
--- a/include/utilities/counter.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/utilities/counter.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,68 +1,85 @@
/*
* Author: Dmitriy Morozov
- * Department of Computer Science, Duke University, 2005 -- 2006
+ * Department of Computer Science, Duke University, 2005 -- 2007
*/
#ifndef __COUNTER_H__
#define __COUNTER_H__
-/* This should be integrated with the logging facilities. Written in the same framework. */
+
+#ifndef COUNTERS
+ #define GetCounter(path) 0
+ #define Count(x)
+ #define CountNum(x,y)
+ #define CountBy(x,y)
+ #define CountNumBy(x,y,z)
+ #define SetFrequency(x, freq)
+ #define SetTrigger(x, y)
+ #define Print(x)
+#else // COUNTERS
+ #define GetCounter(path) get_counter(path)
+ #define Count(x) do { x->count++; if ((x->count % x->frequency == 0)) x->trigger->print(); } while (0)
+ #define CountNum(x,y) do { x->subcount[y]++; } while (0)
+ #define CountBy(x,y) do { x->count += y; } while (0)
+ #define CountNumBy(x,y,z) do { x->subcount[y] += z; } while (0)
+ #define SetFrequency(x, freq) do { x->frequency = freq; } while (0)
+ #define SetTrigger(x, y) do { x->trigger = y; } while(0)
+ #define Print(x) do { x->trigger->print(); } while(0)
+
#include <map>
#include <string>
#include <iostream>
+#include <limits>
+#include <unistd.h>
-#ifndef COUNTERS
-#define Count(x)
-#define CountNum(x,y)
-#else // COUNTERS
-#define Count(x) counters.inc(x)
-#define CountNum(x,y) counters.inc(x,y)
-#endif
-
-typedef unsigned long CounterType;
-typedef std::string KeyType;
-
-class CounterFactory
+class Counter
{
- private:
- typedef std::map<int, CounterType> CountersMap;
- typedef std::map<KeyType, CountersMap> KeyMap;
- KeyMap ctrs;
-
- public:
- ~CounterFactory()
- {
-#ifdef COUNTERS
- std::cout << "Counters:" << std::endl;
- for (KeyMap::const_iterator cur = ctrs.begin(); cur != ctrs.end(); ++cur)
- {
- std::cout << cur->first << ": ";
- if (cur->second.size() == 1)
- {
- std::cout << cur->second.begin()->second << std::endl;
- continue;
- }
- std::cout << std::endl;
- for (CountersMap::const_iterator ccur = cur->second.begin();
- ccur != cur->second.end();
- ++ccur)
- std::cout << " " << ccur->first << ": " << ccur->second << std::endl;
- }
+ public:
+ typedef unsigned long CounterType;
+ typedef std::map<std::string, Counter*> SubCounterMap;
+ typedef std::map<int, CounterType> SubCountMap;
+
+ public:
+ CounterType count;
+ CounterType frequency;
+ SubCountMap subcount;
+ Counter* trigger;
+
+ public:
+ Counter(const std::string& full_name = "",
+ CounterType freq = std::numeric_limits<CounterType>::max());
+ ~Counter();
+
+ Counter* get_child(const std::string& path, std::string::size_type pos);
+ void print();
+
+ private:
+ SubCounterMap subcounters_;
+ std::string full_name_;
+
+ static const char* start_color;
+ static const char* finish_color;
+ static const char green_color[];
+ static const char normal_color[];
+ static const char empty_string[];
+};
+const char Counter::green_color[] = "\033[32m";
+const char Counter::normal_color[] = "\033[0m";
+const char Counter::empty_string[] = "";
+const char* Counter::start_color = 0;
+const char* Counter::finish_color = 0;
+
+static Counter rootCounter;
+
+Counter* get_counter(const char* path)
+{
+ return rootCounter.get_child(path, 0);
+}
+
+#include "counter.hpp"
+
#endif // COUNTERS
- }
- void inc(const KeyType& key, int num = 0)
- {
- ctrs[key][num]++;
- }
-
- CounterType lookup(const KeyType& key, int num = 0) const
- {
- return const_cast<KeyMap&>(ctrs)[key][num];
- }
-};
-
-static CounterFactory counters;
#endif // __COUNTER_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/counter.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,69 @@
+#include <ctime>
+#include <cstdio>
+
+Counter::
+Counter(const std::string& full_name,
+ CounterType freq):
+ count(0), frequency(freq), trigger(this), full_name_(full_name)
+{
+ if (isatty(STDOUT_FILENO))
+ {
+ start_color = green_color;
+ finish_color = normal_color;
+ }
+ else
+ { start_color = finish_color = empty_string; }
+}
+
+
+Counter*
+Counter::
+get_child(const std::string& path, std::string::size_type pos)
+{
+ if (pos >= path.size())
+ return this;
+
+ std::string::size_type slash_pos = path.find('/', pos);
+ if (slash_pos == std::string::npos)
+ slash_pos = path.size();
+
+ std::string child_name = path.substr(pos, slash_pos - pos);
+ SubCounterMap::iterator child = subcounters_.find(child_name);
+
+ if (child != subcounters_.end())
+ return child->second->get_child(path, slash_pos + 1);
+ else
+ return (subcounters_[child_name] = new Counter(path.substr(0, slash_pos)))->get_child(path, slash_pos + 1);
+}
+
+Counter::
+~Counter()
+{
+ if (full_name_ == "" && (!subcounters_.empty() || count))
+ print();
+
+ for (SubCounterMap::iterator cur = subcounters_.begin(); cur != subcounters_.end(); ++cur)
+ delete cur->second;
+}
+
+inline
+void
+Counter::
+print()
+{
+ time_t rawtime; time(&rawtime);
+ struct tm* timeinfo = localtime(&rawtime);
+
+ printf("%s(%02i:%02i:%02i)%s ",
+ start_color,
+ timeinfo->tm_hour,
+ timeinfo->tm_min,
+ timeinfo->tm_sec,
+ finish_color);
+ std::cout << "Counter [" << full_name_ << "]: " << count << std::endl;
+ for (SubCountMap::const_iterator cur = subcount.begin(); cur != subcount.end(); ++cur)
+ std::cout << " " << cur->first << ": " << cur->second << std::endl;
+ for (SubCounterMap::iterator cur = subcounters_.begin(); cur != subcounters_.end(); ++cur)
+ cur->second->print();
+}
+
--- a/include/utilities/debug.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#ifndef DEBUG_H
-#define DEBUG_H
-
-#ifndef CWDEBUG
-
-#include <iostream> // std::cerr
-#include <cstdlib> // std::exit, EXIT_FAILURE
-#include <cassert>
-
-#define AllocTag1(p)
-#define AllocTag2(p, desc)
-#define AllocTag_dynamic_description(p, data)
-#define AllocTag(p, data)
-#define Debug(STATEMENT...)
-#define Dout(cntrl, data)
-#define DoutFatal(cntrl, data) LibcwDoutFatal(, , cntrl, data)
-#define ForAllDebugChannels(STATEMENT...)
-#define ForAllDebugObjects(STATEMENT...)
-#define LibcwDebug(dc_namespace, STATEMENT...)
-#define LibcwDout(dc_namespace, d, cntrl, data)
-#define LibcwDoutFatal(dc_namespace, d, cntrl, data) do { ::std::cerr << data << ::std::endl; ::std::exit(EXIT_FAILURE); } while(1)
-#define LibcwdForAllDebugChannels(dc_namespace, STATEMENT...)
-#define LibcwdForAllDebugObjects(dc_namespace, STATEMENT...)
-#define NEW(x) new x
-#define CWDEBUG_ALLOC 0
-#define CWDEBUG_MAGIC 0
-#define CWDEBUG_LOCATION 0
-#define CWDEBUG_LIBBFD 0
-#define CWDEBUG_DEBUG 0
-#define CWDEBUG_DEBUGOUTPUT 0
-#define CWDEBUG_DEBUGM 0
-#define CWDEBUG_DEBUGT 0
-#define CWDEBUG_MARKER 0
-#define AssertMsg(TEST,MSG)
-//#define AssertMsg(TEST,STRM,MSG)
-
-
-#else // CWDEBUG
-
-// This must be defined before <libcwd/debug.h> is included and must be the
-// name of the namespace containing your `dc' (Debug Channels) namespace
-// (see below). You can use any namespace(s) you like, except existing
-// namespaces (like ::, ::std and ::libcwd).
-#define DEBUGCHANNELS ::dionysus::debug::channels
-#include <libcwd/debug.h>
-
-namespace dionysus
-{
- namespace debug
- {
- void init(void); // Initialize debugging code from main().
- void init_thread(void); // Initialize debugging code from new threads.
-
- namespace channels // This is the DEBUGCHANNELS namespace, see above.
- {
- namespace dc // 'dc' is defined inside DEBUGCHANNELS.
- {
- using namespace libcwd::channels::dc;
- using libcwd::channel_ct;
-
- // Add the declaration of new debug channels here
- // and add their definition in a custom debug.cc file.
- extern channel_ct filtration;
- extern channel_ct transpositions;
- extern channel_ct vineyard;
- extern channel_ct cycle;
- extern channel_ct lsfiltration;
- extern channel_ct orderlist;
- } // namespace dc
- } // namespace DEBUGCHANNELS
- }
-}
-
-
-#define AssertMsg(TEST,MSG) \
- ( (TEST) ? (void)0 \
- : (std::cerr << __FILE__ ":" << __LINE__ \
- << ": Assertion failed " #TEST \
- << " - " << MSG << std::endl,abort()))
-/*
-#define AssertMsg(TEST,STRM,MSG) \
- ( (TEST) ? (void)0 \
- : (DoutFatal(STRM, __FILE__ "(" << __LINE__ \
- << "): Assertion failed " #TEST \
- << MSG << std::endl)))
-*/
-
-#endif // CWDEBUG
-
-#endif // DEBUG_H
--- a/include/utilities/eventqueue.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/utilities/eventqueue.h Tue Jun 27 09:37:05 2017 -0700
@@ -2,60 +2,223 @@
#define __EVENTQUEUE_H__
#include <list>
+#include <vector>
#include <functional>
+
#include <boost/utility.hpp>
+#include <boost/iterator/iterator_adaptor.hpp>
+namespace b = boost;
+
+
+#include <utilities/log.h>
+#ifdef LOGGING
+static rlog::RLogChannel* rlEventQueue = DEF_CHANNEL("utilities/eventqueue", rlog::Log_Debug);
+#endif // LOGGING
+
+#include <utilities/binaryheap.h>
#include <iostream>
-#include <cassert> // TODO: switch to internal debugging system
#include <string>
-
-// TODO: change inefficient list-based implementation to something heap-based
-// Need a queue that supports deleting arbitrary items (given by iterator),
-// and maintaining correctness of iterators when different operations (notably
-// remove and update are performed)
+#include <algorithm>
template<class Event_, class EventComparison_>
class EventQueue
{
+ public:
+ typedef Event_ Event;
+ typedef EventComparison_ EventComparison;
- public:
- typedef Event_ Event;
- typedef EventComparison_ EventComparison;
+ struct HeapNode;
+ struct CompareNodes;
+ struct MarkedCompareNodes;
+ struct UpdateNode;
+ class iterator;
+ class const_iterator;
+
+ typedef std::list<HeapNode> QueueRepresentation;
+ typedef std::vector<iterator> EventListHeap;
+
+ EventQueue() {}
+
+ const_iterator top() const { AssertMsg(!empty(), "Queue must not be empty"); return heap_.front(); }
+ iterator top() { AssertMsg(!empty(), "Queue must not be empty"); return heap_.front(); }
+ iterator push(Event e);
+ void pop();
+ void remove(iterator i);
+ void replace(iterator i, Event e);
+ void promoted(iterator i);
+ void demoted(iterator i);
+
+ iterator begin() { return queue_.begin(); }
+ const_iterator begin() const { return queue_.begin(); }
+ iterator end() { return queue_.end(); }
+ const_iterator end() const { return queue_.end(); }
+ bool empty() const { return queue_.empty(); }
+ size_t size() const { return heap_.size(); }
+
+ std::ostream& print(std::ostream& out, const std::string& prefix) const;
+
+ private:
+ QueueRepresentation queue_;
+ EventListHeap heap_;
+};
+
+template<class Event_, class EventComparison_>
+struct EventQueue<Event_, EventComparison_>::HeapNode
+{
+ HeapNode(const Event& e): e_(e) {}
+
+ size_t heap_position_;
+ Event e_;
+};
+
+template<class Event_, class EventComparison_>
+class EventQueue<Event_, EventComparison_>::iterator:
+ public boost::iterator_adaptor<iterator,
+ typename QueueRepresentation::iterator,
+ Event>
+{
+ public:
+ iterator():
+ iterator::iterator_adaptor_(0) {}
- typedef std::list<Event> QueueRepresentation;
- typedef typename QueueRepresentation::iterator iterator;
- typedef typename QueueRepresentation::const_iterator const_iterator;
-
- EventQueue() {}
-
- const_iterator top() const { assert(!empty()); return queue_.begin(); }
- iterator top() { assert(!empty()); return queue_.begin(); }
- iterator push(Event e) { queue_.push_front(e); iterator i = top(); update(i); return i; }
- void pop() { assert(!empty()); queue_.erase(queue_.begin()); }
- void remove(iterator i) { queue_.erase(i); }
- void update(iterator i);
+ iterator(typename QueueRepresentation::iterator i):
+ iterator::iterator_adaptor_(i) {}
+
+ using iterator::iterator_adaptor_::base;
+
+ typename iterator::iterator_adaptor_::reference
+ dereference() const { return base()->e_; }
+};
+
+template<class Event_, class EventComparison_>
+class EventQueue<Event_, EventComparison_>::const_iterator:
+ public boost::iterator_adaptor<const_iterator,
+ typename QueueRepresentation::const_iterator,
+ Event,
+ b::use_default,
+ const Event&>
+{
+ public:
+ const_iterator():
+ const_iterator::iterator_adaptor_(0) {}
+
+ const_iterator(iterator i):
+ const_iterator::iterator_adaptor_(i.base()) {}
+
+ const_iterator(typename QueueRepresentation::const_iterator i):
+ const_iterator::iterator_adaptor_(i) {}
+
+ using const_iterator::iterator_adaptor_::base;
- iterator end() { return queue_.end(); }
- const_iterator end() const { return queue_.end(); }
- bool empty() const { return queue_.empty(); }
- size_t size() const { return queue_.size(); }
+ typename const_iterator::iterator_adaptor_::reference
+ dereference() const { return base()->e_; }
+};
+
+template<class Event_, class EventComparison_>
+struct EventQueue<Event_, EventComparison_>::UpdateNode
+{
+ void operator()(iterator i, size_t pos) const { i.base()->heap_position_ = pos; }
+};
- std::ostream& print(std::ostream& out, const std::string& prefix) const;
+template<class Event_, class EventComparison_>
+struct EventQueue<Event_, EventComparison_>::CompareNodes
+{
+ bool operator()(iterator i, iterator j) const { return EventComparison()(*j, *i); }
+};
- private:
- QueueRepresentation queue_;
+template<class Event_, class EventComparison_>
+struct EventQueue<Event_, EventComparison_>::MarkedCompareNodes
+{
+ MarkedCompareNodes(iterator i): i_(i) {}
+ bool operator()(iterator i, iterator j) const
+ {
+ // i_ is less than everything else
+ if (i == i_)
+ return false;
+ if (j == i_)
+ return true;
+ return EventComparison()(*j, *i);
+ }
+ iterator i_;
};
template<class Event_, class EventComparison_>
+typename EventQueue<Event_, EventComparison_>::iterator
+EventQueue<Event_, EventComparison_>::
+push(Event e)
+{
+ queue_.push_back(e);
+ iterator i = b::prior(queue_.end());
+ heap_.push_back(i);
+ std::push_heap(heap_.begin(), heap_.end(), CompareNodes(), UpdateNode());
+
+ return i;
+}
+
+template<class Event_, class EventComparison_>
+void
+EventQueue<Event_, EventComparison_>::
+pop()
+{
+ AssertMsg(!empty(), "Queue must not be empty");
+ std::pop_heap(heap_.begin(), heap_.end(), CompareNodes(), UpdateNode());
+ queue_.erase(heap_.back().base()); heap_.pop_back();
+}
+
+template<class Event_, class EventComparison_>
void
EventQueue<Event_, EventComparison_>::
-update(iterator i)
+remove(iterator i)
+{
+ MarkedCompareNodes mcmp(i);
+ std::update_heap_pos(heap_.begin(), heap_.end(), heap_.begin() + i.base()->heap_position_, mcmp, UpdateNode());
+ std::pop_heap(heap_.begin(), heap_.end(), mcmp, UpdateNode());
+ AssertMsg(heap_.back() == i, "i should be in the back");
+ queue_.erase(heap_.back().base());
+ heap_.pop_back();
+}
+
+template<class Event_, class EventComparison_>
+void
+EventQueue<Event_, EventComparison_>::
+replace(iterator i, Event e)
{
- QueueRepresentation tmp;
- tmp.splice(tmp.end(), queue_, i);
- iterator pos = std::find_if(queue_.begin(), queue_.end(), std::not1(std::bind2nd(EventComparison(), *i)));
- queue_.splice(pos, tmp);
+
+ if (EventComparison()(*i, e))
+ {
+ *i = e;
+ promoted(i);
+ } else
+ {
+ *i = e;
+ demoted(i);
+ }
+}
+
+template<class Event_, class EventComparison_>
+void
+EventQueue<Event_, EventComparison_>::
+promoted(iterator i)
+{
+ std::update_heap_pos(heap_.begin(), heap_.end(), heap_.begin() + i.base()->heap_position_, CompareNodes(), UpdateNode());
+}
+
+template<class Event_, class EventComparison_>
+void
+EventQueue<Event_, EventComparison_>::
+demoted(iterator i)
+{
+ // Bring to front
+ MarkedCompareNodes mcmp(i);
+ std::update_heap_pos(heap_.begin(), heap_.end(), heap_.begin() + i.base()->heap_position_, mcmp, UpdateNode());
+
+ // Bring to back
+ std::pop_heap(heap_.begin(), heap_.end(), mcmp, UpdateNode());
+
+ // Find new place
+ std::update_heap_pos(heap_.begin(), heap_.end(), heap_.begin() + i.base()->heap_position_, CompareNodes(), UpdateNode());
}
template<class Event_, class EventComparison_>
@@ -63,9 +226,9 @@
EventQueue<Event_, EventComparison_>::
print(std::ostream& out, const std::string& prefix) const
{
- for (typename QueueRepresentation::const_iterator cur = queue_.begin(); cur != queue_.end(); ++cur)
- (*cur)->print(out << prefix) << std::endl;
- return out;
+ for (typename EventListHeap::const_iterator cur = heap_.begin(); cur != heap_.end(); ++cur)
+ out << prefix << **cur << std::endl;
+ return out;
}
#endif // __EVENTQUEUE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/indirect.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,185 @@
+#ifndef __INDIRECT_H__
+#define __INDIRECT_H__
+
+#include <boost/iterator/iterator_adaptor.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include <iterator>
+
+// TODO: write documentation
+
+/**************
+ * Comparison *
+ **************/
+
+template<class Comparison_>
+struct IndirectComparison
+{
+ typedef Comparison_ Comparison;
+
+ IndirectComparison(const Comparison& cmp):
+ cmp_(cmp)
+ {}
+
+ template<class Iterator>
+ bool operator()(Iterator a, Iterator b) const
+ { return cmp_(*a, *b); }
+
+ const Comparison& cmp_;
+};
+template<class Comparison>
+IndirectComparison<Comparison> make_indirect_comparison(const Comparison& cmp)
+{ return IndirectComparison<Comparison>(cmp); }
+
+
+template<class Comparison_>
+class FirstComparison
+{
+ public:
+ typedef Comparison_ Comparison;
+
+ FirstComparison(Comparison cmp):
+ cmp_(cmp) {}
+
+ template<class Pair>
+ bool operator()(const Pair& a, const Pair& b) const
+ { return cmp_(a.first, b.first); }
+
+ private:
+ Comparison cmp_;
+};
+template<class Comparison>
+FirstComparison<Comparison> make_first_comparison(const Comparison& cmp)
+{ return FirstComparison<Comparison>(cmp); }
+
+
+template<class Comparison>
+struct ThreeOutcomeCompare: public Comparison
+{
+ typedef typename Comparison::first_argument_type first_argument_type;
+ typedef typename Comparison::second_argument_type second_argument_type;
+
+ ThreeOutcomeCompare(const Comparison& cmp = Comparison()): Comparison(cmp) {}
+
+ int compare(first_argument_type a, second_argument_type b) const
+ { if (this->operator()(a,b)) return -1;
+ else if (this->operator()(b,a)) return 1;
+ else return 0;
+ }
+};
+
+template<class Evaluator_>
+class ThroughEvaluatorComparison
+{
+ public:
+ typedef Evaluator_ Evaluator;
+
+ ThroughEvaluatorComparison(const Evaluator& eval):
+ eval_(eval) {}
+
+ template<class T>
+ bool operator()(T a, T b) const { return (eval_(a) < eval_(b)); }
+
+ private:
+ const Evaluator& eval_;
+};
+
+
+/*************
+ * Iterators *
+ *************/
+
+// Iterates over the difference of the two sorted sequences, dereferencing into the first sequence
+template<class Iterator1, class Iterator2, class StrictWeakOrdering>
+class difference_iterator: public boost::iterator_facade<difference_iterator<Iterator1, Iterator2, StrictWeakOrdering>,
+ typename std::iterator_traits<Iterator1>::value_type,
+ boost::forward_traversal_tag>
+{
+ public:
+ typedef typename std::iterator_traits<Iterator1>::reference reference;
+
+ difference_iterator(Iterator1 cur1, Iterator1 end1,
+ Iterator2 cur2, Iterator2 end2,
+ const StrictWeakOrdering& cmp = StrictWeakOrdering()):
+ cur1_(cur1), end1_(end1), cur2_(cur2), end2_(end2), cmp_(cmp) { catchup(); }
+
+ private:
+ friend class boost::iterator_core_access;
+
+ void increment() { ++cur1_; catchup(); }
+ bool equal(const difference_iterator& other) const { return (cur1_ == other.cur1_) && (cur2_ == other.cur2_); }
+ reference dereference() const { return *cur1_; }
+
+ private:
+ Iterator1 cur1_, end1_;
+ Iterator2 cur2_, end2_;
+ const StrictWeakOrdering& cmp_;
+
+ private:
+ void catchup()
+ {
+ while ((cur1_ != end1_) && (cur2_ != end2_))
+ {
+ if (cmp_(*cur1_, *cur2_)) break;
+ else if (cmp_(*cur2_, *cur1_)) ++cur2_;
+ else { ++cur1_; ++cur2_; }
+ }
+
+ if (cur1_ == end1_) cur2_ = end2_;
+ }
+};
+
+template<class Iterator1, class Iterator2, class StrictWeakOrdering>
+difference_iterator<Iterator1, Iterator2, StrictWeakOrdering>
+make_difference_iterator(Iterator1 cur1, Iterator1 end1, Iterator2 cur2, Iterator2 end2, const StrictWeakOrdering& cmp)
+{ return difference_iterator<Iterator1, Iterator2, StrictWeakOrdering>(cur1, end1, cur2, end2, cmp); }
+
+// Iterates over the intersection of the two sorted sequences, dereferencing into the first sequence
+template<class Iterator1, class Iterator2, class StrictWeakOrdering>
+class intersection_iterator: public boost::iterator_facade<intersection_iterator<Iterator1, Iterator2, StrictWeakOrdering>,
+ typename std::iterator_traits<Iterator1>::value_type,
+ boost::forward_traversal_tag>
+{
+ public:
+ typedef typename std::iterator_traits<Iterator1>::reference reference;
+
+ intersection_iterator(Iterator1 cur1, Iterator1 end1,
+ Iterator2 cur2, Iterator2 end2,
+ const StrictWeakOrdering& cmp = StrictWeakOrdering()):
+ cur1_(cur1), end1_(end1), cur2_(cur2), end2_(end2), cmp_(cmp) { catchup(); }
+
+ private:
+ friend class boost::iterator_core_access;
+
+ void increment() { ++cur1_; ++cur2_; catchup(); }
+ bool equal(const intersection_iterator& other) const { return (cur1_ == other.cur1_) && (cur2_ == other.cur2_); }
+ reference dereference() const { return *cur1_; }
+
+ private:
+ Iterator1 cur1_, end1_;
+ Iterator2 cur2_, end2_;
+ const StrictWeakOrdering& cmp_;
+
+ private:
+ void catchup()
+ {
+ while ((cur1_ != end1_) && (cur2_ != end2_))
+ {
+ if (cmp_(*cur1_, *cur2_)) ++cur1_;
+ else if (cmp_(*cur2_, *cur1_)) ++cur2_;
+ else break;
+ }
+
+ if ((cur1_ == end1_) || (cur2_ == end2_))
+ {
+ cur1_ = end1_;
+ cur2_ = end2_;
+ }
+ }
+};
+
+template<class Iterator1, class Iterator2, class StrictWeakOrdering>
+intersection_iterator<Iterator1, Iterator2, StrictWeakOrdering>
+make_intersection_iterator(Iterator1 cur1, Iterator1 end1, Iterator2 cur2, Iterator2 end2, const StrictWeakOrdering& cmp)
+{ return intersection_iterator<Iterator1, Iterator2, StrictWeakOrdering>(cur1, end1, cur2, end2, cmp); }
+
+#endif // __INDIRECT_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/log.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,68 @@
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include <unistd.h> // for STDOUT_FILENO and STDERR_FILENO
+
+#if LOGGING
+
+#define RLOG_COMPONENT dionysus
+
+#include <rlog/rlog.h>
+#include <rlog/RLogChannel.h>
+#include <rlog/StdioNode.h>
+#include <sstream>
+
+#undef RLOG_SECTION
+#define RLOG_SECTION
+
+template<class T>
+std::string tostring(const T& t) { std::ostringstream out; out << t; return out.str(); }
+template<class T>
+std::string intostring(const T& t) { std::ostringstream out; t.operator<<(out); return out.str(); }
+
+#define AssertMsg(cond, message, ...) do { if (!(cond)) { rError(message, ##__VA_ARGS__); rAssertSilent(cond); } } while (0)
+
+#else // LOGGING
+
+#define rDebug(...)
+#define rInfo(...)
+#define rWarning(...)
+#define rError(...)
+#define rLog(...)
+
+#define rAssert(...)
+#define rAssertSilent(...)
+
+#define DEF_CHANNEL(...) 0
+#define RLOG_CHANNEL(...) 0
+
+#define AssertMsg(cond, ...)
+
+// To avoid undefined errors for RLogChannel, we create a dummy namespace
+namespace rlog
+{
+ typedef void RLogChannel;
+
+ class StdioNode
+ {
+ public:
+ StdioNode(int,int) {}
+ void subscribeTo(RLogChannel*) {}
+
+ static const int OutputColor = 0;
+ static const int OutputChannel = 0;
+ };
+}
+
+#endif // LOGGING
+
+static rlog::StdioNode stdoutLog(STDOUT_FILENO,
+ rlog::StdioNode::OutputColor +
+ rlog::StdioNode::OutputChannel);
+
+static rlog::StdioNode stderrLog(STDERR_FILENO,
+ rlog::StdioNode::OutputColor +
+ rlog::StdioNode::OutputChannel);
+
+
+#endif //__LOG_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/memory.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,43 @@
+#ifndef __MEMORY_H__
+#define __MEMORY_H__
+
+#if LOGGING // TODO: add check for Linux (and preferably the right version of Linux)
+
+#include "log.h"
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <unistd.h>
+
+static rlog::RLogChannel* rlMemory = DEF_CHANNEL("memory", rlog::Log_Debug);
+
+unsigned report_memory()
+{
+ pid_t pid = getpid();
+ std::stringstream smaps_name;
+ smaps_name << "/proc/" << pid << "/smaps";
+ std::ifstream in(smaps_name.str().c_str());
+
+ std::string str;
+ unsigned memory = 0, value;
+ while (in)
+ {
+ in >> str;
+ if (std::string(str, 0, 7) == "Private")
+ {
+ in >> value;
+ memory += value;
+ }
+ }
+ rLog(rlMemory, "Private memory usage: %d kB", memory);
+
+ return memory;
+}
+
+#else
+
+unsigned report_memory() { return 0; }
+
+#endif // LOGGING
+
+#endif // __MEMORY_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/munkres/matrix.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2007 John Weaver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(_MATRIX_H_)
+#define _MATRIX_H_
+
+template <class T>
+class Matrix {
+public:
+ Matrix();
+ Matrix(int rows, int columns);
+ Matrix(const Matrix<T> &other);
+ Matrix<T> & operator= (const Matrix<T> &other);
+ ~Matrix();
+ // all operations except product modify the matrix in-place.
+ void resize(int rows, int columns);
+ void identity(void);
+ void clear(void);
+ T& operator () (int x, int y);
+ T trace(void);
+ Matrix<T>& transpose(void);
+ Matrix<T> product(Matrix<T> &other);
+ int minsize(void) {
+ return ((m_rows < m_columns) ? m_rows : m_columns);
+ }
+ int columns(void) {
+ return m_columns;
+ }
+ int rows(void) {
+ return m_rows;
+ }
+private:
+ T **m_matrix;
+ int m_rows;
+ int m_columns;
+};
+
+#include "matrix.hpp"
+
+#endif /* !defined(_MATRIX_H_) */
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/munkres/matrix.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2007 John Weaver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "matrix.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <algorithm>
+
+/*export*/ template <class T>
+Matrix<T>::Matrix() {
+ m_rows = 0;
+ m_columns = 0;
+ m_matrix = NULL;
+}
+
+/*export*/ template <class T>
+Matrix<T>::Matrix(const Matrix<T> &other) {
+ if ( other.m_matrix != NULL ) {
+ // copy arrays
+ m_matrix = NULL;
+ resize(other.m_rows, other.m_columns);
+ for ( int i = 0 ; i < m_rows ; i++ )
+ for ( int j = 0 ; j < m_columns ; j++ )
+ m_matrix[i][j] = other.m_matrix[i][j];
+ } else {
+ m_matrix = NULL;
+ m_rows = 0;
+ m_columns = 0;
+ }
+}
+
+/*export*/ template <class T>
+Matrix<T>::Matrix(int rows, int columns) {
+ m_matrix = NULL;
+ resize(rows, columns);
+}
+
+/*export*/ template <class T>
+Matrix<T> &
+Matrix<T>::operator= (const Matrix<T> &other) {
+ if ( other.m_matrix != NULL ) {
+ // copy arrays
+ resize(other.m_rows, other.m_columns);
+ for ( int i = 0 ; i < m_rows ; i++ )
+ for ( int j = 0 ; j < m_columns ; j++ )
+ m_matrix[i][j] = other.m_matrix[i][j];
+ } else {
+ // free arrays
+ for ( int i = 0 ; i < m_columns ; i++ )
+ delete [] m_matrix[i];
+
+ delete [] m_matrix;
+
+ m_matrix = NULL;
+ m_rows = 0;
+ m_columns = 0;
+ }
+
+ return *this;
+}
+
+/*export*/ template <class T>
+Matrix<T>::~Matrix() {
+ if ( m_matrix != NULL ) {
+ // free arrays
+ for ( int i = 0 ; i < m_rows ; i++ )
+ delete [] m_matrix[i];
+
+ delete [] m_matrix;
+ }
+ m_matrix = NULL;
+}
+
+/*export*/ template <class T>
+void
+Matrix<T>::resize(int rows, int columns) {
+ if ( m_matrix == NULL ) {
+ // alloc arrays
+ m_matrix = new T*[rows]; // rows
+ for ( int i = 0 ; i < rows ; i++ )
+ m_matrix[i] = new T[columns]; // columns
+
+ m_rows = rows;
+ m_columns = columns;
+ clear();
+ } else {
+ // save array pointer
+ T **new_matrix;
+ // alloc new arrays
+ new_matrix = new T*[rows]; // rows
+ for ( int i = 0 ; i < rows ; i++ ) {
+ new_matrix[i] = new T[columns]; // columns
+ for ( int j = 0 ; j < columns ; j++ )
+ new_matrix[i][j] = 0;
+ }
+
+ // copy data from saved pointer to new arrays
+ int minrows = std::min<int>(rows, m_rows);
+ int mincols = std::min<int>(columns, m_columns);
+ for ( int x = 0 ; x < minrows ; x++ )
+ for ( int y = 0 ; y < mincols ; y++ )
+ new_matrix[x][y] = m_matrix[x][y];
+
+ // delete old arrays
+ if ( m_matrix != NULL ) {
+ for ( int i = 0 ; i < m_rows ; i++ )
+ delete [] m_matrix[i];
+
+ delete [] m_matrix;
+ }
+
+ m_matrix = new_matrix;
+ }
+
+ m_rows = rows;
+ m_columns = columns;
+}
+
+/*export*/ template <class T>
+void
+Matrix<T>::identity() {
+ assert( m_matrix != NULL );
+
+ clear();
+
+ int x = std::min<int>(m_rows, m_columns);
+ for ( int i = 0 ; i < x ; i++ )
+ m_matrix[i][i] = 1;
+}
+
+/*export*/ template <class T>
+void
+Matrix<T>::clear() {
+ assert( m_matrix != NULL );
+
+ for ( int i = 0 ; i < m_rows ; i++ )
+ for ( int j = 0 ; j < m_columns ; j++ )
+ m_matrix[i][j] = 0;
+}
+
+/*export*/ template <class T>
+T
+Matrix<T>::trace() {
+ assert( m_matrix != NULL );
+
+ T value = 0;
+
+ int x = std::min<int>(m_rows, m_columns);
+ for ( int i = 0 ; i < x ; i++ )
+ value += m_matrix[i][i];
+
+ return value;
+}
+
+/*export*/ template <class T>
+Matrix<T>&
+Matrix<T>::transpose() {
+ assert( m_rows > 0 );
+ assert( m_columns > 0 );
+
+ int new_rows = m_columns;
+ int new_columns = m_rows;
+
+ if ( m_rows != m_columns ) {
+ // expand matrix
+ int m = std::max<int>(m_rows, m_columns);
+ resize(m,m);
+ }
+
+ for ( int i = 0 ; i < m_rows ; i++ ) {
+ for ( int j = i+1 ; j < m_columns ; j++ ) {
+ T tmp = m_matrix[i][j];
+ m_matrix[i][j] = m_matrix[j][i];
+ m_matrix[j][i] = tmp;
+ }
+ }
+
+ if ( new_columns != new_rows ) {
+ // trim off excess.
+ resize(new_rows, new_columns);
+ }
+
+ return *this;
+}
+
+/*export*/ template <class T>
+Matrix<T>
+Matrix<T>::product(Matrix<T> &other) {
+ assert( m_matrix != NULL );
+ assert( other.m_matrix != NULL );
+ assert ( m_columns == other.m_rows );
+
+ Matrix<T> out(m_rows, other.m_columns);
+
+ for ( int i = 0 ; i < out.m_rows ; i++ ) {
+ for ( int j = 0 ; j < out.m_columns ; j++ ) {
+ for ( int x = 0 ; x < m_columns ; x++ ) {
+ out(i,j) += m_matrix[i][x] * other.m_matrix[x][j];
+ }
+ }
+ }
+
+ return out;
+}
+
+/*export*/ template <class T>
+T&
+Matrix<T>::operator ()(int x, int y) {
+ assert ( x >= 0 );
+ assert ( y >= 0 );
+ assert ( x < m_rows );
+ assert ( y < m_columns );
+ assert ( m_matrix != NULL );
+ return m_matrix[x][y];
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/munkres/munkres.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2007 John Weaver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "munkres.h"
+
+#include <iostream>
+#include <cmath>
+
+bool
+Munkres::find_uncovered_in_matrix(double item, int &row, int &col) {
+ for ( row = 0 ; row < matrix.rows() ; row++ )
+ if ( !row_mask[row] )
+ for ( col = 0 ; col < matrix.columns() ; col++ )
+ if ( !col_mask[col] )
+ if ( matrix(row,col) == item )
+ return true;
+
+ return false;
+}
+
+bool
+Munkres::pair_in_list(const std::pair<int,int> &needle, const std::list<std::pair<int,int> > &haystack) {
+ for ( std::list<std::pair<int,int> >::const_iterator i = haystack.begin() ; i != haystack.end() ; i++ ) {
+ if ( needle == *i )
+ return true;
+ }
+
+ return false;
+}
+
+int
+Munkres::step1(void) {
+ for ( int row = 0 ; row < matrix.rows() ; row++ )
+ for ( int col = 0 ; col < matrix.columns() ; col++ )
+ if ( matrix(row,col) == 0 ) {
+ bool isstarred = false;
+ for ( int nrow = 0 ; nrow < matrix.rows() ; nrow++ )
+ if ( mask_matrix(nrow,col) == STAR ) {
+ isstarred = true;
+ break;
+ }
+
+ if ( !isstarred ) {
+ for ( int ncol = 0 ; ncol < matrix.columns() ; ncol++ )
+ if ( mask_matrix(row,ncol) == STAR ) {
+ isstarred = true;
+ break;
+ }
+ }
+
+ if ( !isstarred ) {
+ mask_matrix(row,col) = STAR;
+ }
+ }
+
+ return 2;
+}
+
+int
+Munkres::step2(void) {
+ int rows = matrix.rows();
+ int cols = matrix.columns();
+ int covercount = 0;
+ for ( int row = 0 ; row < rows ; row++ )
+ for ( int col = 0 ; col < cols ; col++ )
+ if ( mask_matrix(row,col) == STAR ) {
+ col_mask[col] = true;
+ covercount++;
+ }
+
+ int k = matrix.minsize();
+
+ if ( covercount >= k ) {
+#ifdef DEBUG
+ std::cout << "Final cover count: " << covercount << std::endl;
+#endif
+ return 0;
+ }
+
+#ifdef DEBUG
+ std::cout << "Munkres matrix has " << covercount << " of " << k << " Columns covered:" << std::endl;
+ for ( int row = 0 ; row < rows ; row++ ) {
+ for ( int col = 0 ; col < cols ; col++ ) {
+ std::cout.width(8);
+ std::cout << matrix(row,col) << ",";
+ }
+ std::cout << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
+
+ return 3;
+}
+
+int
+Munkres::step3(void) {
+ /*
+ Main Zero Search
+
+ 1. Find an uncovered Z in the distance matrix and prime it. If no such zero exists, go to Step 5
+ 2. If No Z* exists in the row of the Z', go to Step 4.
+ 3. If a Z* exists, cover this row and uncover the column of the Z*. Return to Step 3.1 to find a new Z
+ */
+ if ( find_uncovered_in_matrix(0, saverow, savecol) ) {
+ mask_matrix(saverow,savecol) = PRIME; // prime it.
+ } else {
+ return 5;
+ }
+
+ for ( int ncol = 0 ; ncol < matrix.columns() ; ncol++ )
+ if ( mask_matrix(saverow,ncol) == STAR ) {
+ row_mask[saverow] = true; //cover this row and
+ col_mask[ncol] = false; // uncover the column containing the starred zero
+ return 3; // repeat
+ }
+
+ return 4; // no starred zero in the row containing this primed zero
+}
+
+int
+Munkres::step4(void) {
+ int rows = matrix.rows();
+ int cols = matrix.columns();
+
+ std::list<std::pair<int,int> > seq;
+ // use saverow, savecol from step 3.
+ std::pair<int,int> z0(saverow, savecol);
+ std::pair<int,int> z1(-1,-1);
+ std::pair<int,int> z2n(-1,-1);
+ seq.insert(seq.end(), z0);
+ int row, col = savecol;
+ /*
+ Increment Set of Starred Zeros
+
+ 1. Construct the ``alternating sequence'' of primed and starred zeros:
+
+ Z0 : Unpaired Z' from Step 4.2
+ Z1 : The Z* in the column of Z0
+ Z[2N] : The Z' in the row of Z[2N-1], if such a zero exists
+ Z[2N+1] : The Z* in the column of Z[2N]
+
+ The sequence eventually terminates with an unpaired Z' = Z[2N] for some N.
+ */
+ bool madepair;
+ do {
+ madepair = false;
+ for ( row = 0 ; row < rows ; row++ )
+ if ( mask_matrix(row,col) == STAR ) {
+ z1.first = row;
+ z1.second = col;
+ if ( pair_in_list(z1, seq) )
+ continue;
+
+ madepair = true;
+ seq.insert(seq.end(), z1);
+ break;
+ }
+
+ if ( !madepair )
+ break;
+
+ madepair = false;
+
+ for ( col = 0 ; col < cols ; col++ )
+ if ( mask_matrix(row,col) == PRIME ) {
+ z2n.first = row;
+ z2n.second = col;
+ if ( pair_in_list(z2n, seq) )
+ continue;
+ madepair = true;
+ seq.insert(seq.end(), z2n);
+ break;
+ }
+ } while ( madepair );
+
+ for ( std::list<std::pair<int,int> >::iterator i = seq.begin() ;
+ i != seq.end() ;
+ i++ ) {
+ // 2. Unstar each starred zero of the sequence.
+ if ( mask_matrix(i->first,i->second) == STAR )
+ mask_matrix(i->first,i->second) = NORMAL;
+
+ // 3. Star each primed zero of the sequence,
+ // thus increasing the number of starred zeros by one.
+ if ( mask_matrix(i->first,i->second) == PRIME )
+ mask_matrix(i->first,i->second) = STAR;
+ }
+
+ // 4. Erase all primes, uncover all columns and rows,
+ for ( int row = 0 ; row < mask_matrix.rows() ; row++ )
+ for ( int col = 0 ; col < mask_matrix.columns() ; col++ )
+ if ( mask_matrix(row,col) == PRIME )
+ mask_matrix(row,col) = NORMAL;
+
+ for ( int i = 0 ; i < rows ; i++ ) {
+ row_mask[i] = false;
+ }
+
+ for ( int i = 0 ; i < cols ; i++ ) {
+ col_mask[i] = false;
+ }
+
+ // and return to Step 2.
+ return 2;
+}
+
+int
+Munkres::step5(void) {
+ int rows = matrix.rows();
+ int cols = matrix.columns();
+ /*
+ New Zero Manufactures
+
+ 1. Let h be the smallest uncovered entry in the (modified) distance matrix.
+ 2. Add h to all covered rows.
+ 3. Subtract h from all uncovered columns
+ 4. Return to Step 3, without altering stars, primes, or covers.
+ */
+ double h = 0;
+ for ( int row = 0 ; row < rows ; row++ ) {
+ if ( !row_mask[row] ) {
+ for ( int col = 0 ; col < cols ; col++ ) {
+ if ( !col_mask[col] ) {
+ if ( (h > matrix(row,col) && matrix(row,col) != 0) || h == 0 ) {
+ h = matrix(row,col);
+ }
+ }
+ }
+ }
+ }
+
+ for ( int row = 0 ; row < rows ; row++ )
+ if ( row_mask[row] )
+ for ( int col = 0 ; col < cols ; col++ )
+ matrix(row,col) += h;
+
+ for ( int col = 0 ; col < cols ; col++ )
+ if ( !col_mask[col] )
+ for ( int row = 0 ; row < rows ; row++ )
+ matrix(row,col) -= h;
+
+ return 3;
+}
+
+void
+Munkres::solve(Matrix<double> &m) {
+ // Linear assignment problem solution
+ // [modifies matrix in-place.]
+ // matrix(row,col): row major format assumed.
+
+ // Assignments are remaining 0 values
+ // (extra 0 values are replaced with -1)
+#ifdef DEBUG
+ std::cout << "Munkres input matrix:" << std::endl;
+ for ( int row = 0 ; row < m.rows() ; row++ ) {
+ for ( int col = 0 ; col < m.columns() ; col++ ) {
+ std::cout.width(8);
+ std::cout << m(row,col) << ",";
+ }
+ std::cout << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
+ double highValue = 0;
+ for ( int row = 0 ; row < m.rows() ; row++ ) {
+ for ( int col = 0 ; col < m.columns() ; col++ ) {
+ if ( m(row,col) != INFINITY && m(row,col) > highValue )
+ highValue = m(row,col);
+ }
+ }
+ highValue++;
+
+ for ( int row = 0 ; row < m.rows() ; row++ )
+ for ( int col = 0 ; col < m.columns() ; col++ )
+ if ( m(row,col) == INFINITY )
+ m(row,col) = highValue;
+
+ bool notdone = true;
+ int step = 1;
+
+ this->matrix = m;
+ // STAR == 1 == starred, PRIME == 2 == primed
+ mask_matrix.resize(matrix.rows(), matrix.columns());
+
+ row_mask = new bool[matrix.rows()];
+ col_mask = new bool[matrix.columns()];
+ for ( int i = 0 ; i < matrix.rows() ; i++ ) {
+ row_mask[i] = false;
+ }
+
+ for ( int i = 0 ; i < matrix.columns() ; i++ ) {
+ col_mask[i] = false;
+ }
+
+ while ( notdone ) {
+ switch ( step ) {
+ case 0:
+ notdone = false;
+ break;
+ case 1:
+ step = step1();
+ break;
+ case 2:
+ step = step2();
+ break;
+ case 3:
+ step = step3();
+ break;
+ case 4:
+ step = step4();
+ break;
+ case 5:
+ step = step5();
+ break;
+ }
+ }
+
+ // Store results
+ for ( int row = 0 ; row < matrix.rows() ; row++ )
+ for ( int col = 0 ; col < matrix.columns() ; col++ )
+ if ( mask_matrix(row,col) == STAR )
+ matrix(row,col) = 0;
+ else
+ matrix(row,col) = -1;
+
+#ifdef DEBUG
+ std::cout << "Munkres output matrix:" << std::endl;
+ for ( int row = 0 ; row < matrix.rows() ; row++ ) {
+ for ( int col = 0 ; col < matrix.columns() ; col++ ) {
+ std::cout.width(1);
+ std::cout << matrix(row,col) << ",";
+ }
+ std::cout << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
+ m = matrix;
+
+ delete [] row_mask;
+ delete [] col_mask;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/munkres/munkres.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007 John Weaver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(_MUNKRES_H_)
+#define _MUNKRES_H_
+
+#include "matrix.h"
+
+#include <list>
+#include <utility>
+
+class Munkres {
+public:
+ void solve(Matrix<double> &m);
+private:
+ static const int NORMAL = 0;
+ static const int STAR = 1;
+ static const int PRIME = 2;
+ inline bool find_uncovered_in_matrix(double,int&,int&);
+ inline bool pair_in_list(const std::pair<int,int> &, const std::list<std::pair<int,int> > &);
+ int step1(void);
+ int step2(void);
+ int step3(void);
+ int step4(void);
+ int step5(void);
+ int step6(void);
+ Matrix<int> mask_matrix;
+ Matrix<double> matrix;
+ bool *row_mask;
+ bool *col_mask;
+ int saverow, savecol;
+};
+
+// DM: This is dangerous, but will do for now
+#include "munkres.cpp"
+
+#endif /* !defined(_MUNKRES_H_) */
--- a/include/utilities/orderlist.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/utilities/orderlist.h Tue Jun 27 09:37:05 2017 -0700
@@ -1,5 +1,5 @@
/*
- * Author: Dmitriy Morozov
+ * Author: Dmitriy Morozov
* Department of Computer Science, Duke University, 2006
*
* Implements the simplified order list data strcutre given in ``Two Simplified
@@ -12,15 +12,13 @@
#ifndef __ORDERLIST_H__
#define __ORDERLIST_H__
-#include "sys.h"
-#include "debug.h"
+#include "log.h"
#include <iterator>
#include <iostream>
#include <list>
#include "types.h"
-//#include "counter.h"
#include <boost/utility.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
@@ -44,21 +42,21 @@
class OrderList: public std::list<OrderListNode<T> >
{
public:
- class OrderComparison;
+ class OrderComparison;
/// OrderComparison type
typedef OrderComparison OrderComparison;
-
+
typedef OrderListNode<T> NodeType;
typedef OrderList<T> Self;
typedef std::list<NodeType > Parent;
-
+
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef OrderListIterator<T> iterator;
typedef const_OrderListIterator<T> const_iterator;
-
+
OrderList() {}
~OrderList() { clear(); }
@@ -73,8 +71,8 @@
iterator push_back(const_reference x);
iterator insert(iterator predecessor, const_reference x); ///< Inserts x immediately after predecessor (has to be a valid iterator)
void erase(iterator x) { Parent::erase(x.get_base()); }
-
- void clear() { return Parent::clear(); }
+
+ void clear() { return Parent::clear(); }
bool empty() const { return Parent::empty(); }
SizeType size() const { return Parent::size(); }
iterator begin() { return iterator(Parent::begin()); }
@@ -84,18 +82,18 @@
reference back() { return Parent::back(); }
const_reference back() const { return Parent::back(); }
void pop_back() { return Parent::pop_back(); }
-
+
iterator last() { return iterator(boost::prior(end())); }
const_iterator last() const { return const_iterator(boost::prior(end())); }
/// @}
-
+
/// \name Debugging operations
/// @{
void show_elements() const;
/// @}
private:
- static const float density_threshold = 1.2;
+ static const float density_threshold;
};
/// Basic comparison that LessThan and GreaterThan derive from
@@ -103,8 +101,8 @@
class OrderList<T>::OrderComparison
{
public:
- typedef typename OrderList<T>::const_iterator ComparableType;
- int compare(ComparableType a, ComparableType b) const; /// (-1,0,1) = a (precedes, ==, succeeds) b
+ typedef typename OrderList<T>::const_iterator ComparableType;
+ int compare(ComparableType a, ComparableType b) const; /// (-1,0,1) = a (precedes, ==, succeeds) b
bool operator()(ComparableType a, ComparableType b) const;
};
@@ -115,10 +113,10 @@
OrderListNode(const T& d, unsigned int t):
data(d), tag(t)
{}
-
+
T data;
OrderType tag;
-
+
std::ostream& operator<<(std::ostream& out) const { return out << data << ": " << tag; }
};
@@ -144,7 +142,7 @@
{
private:
struct enabler {};
-
+
public:
typedef typename OrderList<T>::Parent OrderListParent;
typedef boost::iterator_adaptor<OrderListIterator<T>,
@@ -155,12 +153,12 @@
OrderListIterator() {}
OrderListIterator(const typename OrderListParent::iterator& iter):
- OrderListIterator::iterator_adaptor_(iter)
+ OrderListIterator::iterator_adaptor_(iter)
{}
OrderListIterator(const OrderListIterator<T>& other):
- OrderListIterator::iterator_adaptor_(other.base())
+ OrderListIterator::iterator_adaptor_(other.base())
{}
-
+
private:
friend class boost::iterator_core_access;
reference dereference() const { return Parent::base_reference()->data; }
@@ -176,7 +174,7 @@
{
private:
struct enabler {};
-
+
public:
typedef typename OrderList<T>::Parent OrderListParent;
typedef boost::iterator_adaptor<const_OrderListIterator<T>,
@@ -187,13 +185,13 @@
const_OrderListIterator() {}
const_OrderListIterator(const typename OrderListParent::const_iterator& iter):
- const_OrderListIterator::iterator_adaptor_(iter)
+ const_OrderListIterator::iterator_adaptor_(iter)
{}
const_OrderListIterator(const const_OrderListIterator<T>& other):
- const_OrderListIterator::iterator_adaptor_(other.base())
+ const_OrderListIterator::iterator_adaptor_(other.base())
{}
const_OrderListIterator(const OrderListIterator<T>& other):
- const_OrderListIterator::iterator_adaptor_(other.base())
+ const_OrderListIterator::iterator_adaptor_(other.base())
{}
private:
@@ -203,7 +201,6 @@
get_base() { return Parent::base_reference(); }
friend class OrderList<T>;
- friend class OrderList<T>::OrderComparison;
};
--- a/include/utilities/orderlist.hpp Fri Aug 24 16:58:25 2007 -0400
+++ b/include/utilities/orderlist.hpp Tue Jun 27 09:37:05 2017 -0700
@@ -1,5 +1,13 @@
/* Implementations */
+#ifdef LOGGING
+static rlog::RLogChannel* rlOrderList = DEF_CHANNEL("utilities/orderlist", rlog::Log_Debug);
+#endif // LOGGING
+
+// This cannot be in the header file to conform to the C++ standard
+template<class T>
+const float OrderList<T>::density_threshold = 1.2;
+
template<class T>
void
OrderList<T>::
@@ -70,16 +78,16 @@
} while (inv_density * num_elements >= maximum);
++num_elements; // for the extra element inserted
- Dout(dc::orderlist, num_elements << ", " << lower << ", " << upper);
- Dout(dc::orderlist, "prev is at the end: " << (prev == Parent::end()));
- Dout(dc::orderlist, "next is at the end: " << (next == Parent::end()));
+ rLog(rlOrderList, "%i, %i, %i", num_elements, lower, upper);
+ rLog(rlOrderList, "prev is at the end: %i", (prev == Parent::end()));
+ rLog(rlOrderList, "next is at the end: %i", (next == Parent::end()));
// Reorder
AssertMsg((upper - lower + 1)/num_elements > 0, "Spacing between new tags must be non-zero");
for (unsigned int i = 0; i < num_elements; ++i)
{
(++prev)->tag = lower + i*((upper - lower + 1)/num_elements);
- Dout(dc::orderlist, prev->tag);
+ rLog(rlOrderList, "%i", prev->tag);
AssertMsg(prev->tag != 0 || prev == Parent::begin(), "Cannot assign 0 tag except at the beginning of OrderList");
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/property-maps.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,183 @@
+#ifndef __PROPERTY_MAPS_H__
+#define __PROPERTY_MAPS_H__
+
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 104000
+#include <boost/property_map/property_map.hpp>
+#else
+#include <boost/property_map.hpp>
+#endif
+
+#include <boost/iterator/iterator_traits.hpp>
+#include <algorithm>
+#include "utilities/log.h"
+
+
+/* Associative Map */ // FIXME: this needs to have more thought put into it
+template<class UniquePairAssociativeContainer_>
+class AssociativeMap: public boost::associative_property_map<UniquePairAssociativeContainer_>
+{
+ public:
+ typedef boost::associative_property_map<UniquePairAssociativeContainer_> Parent;
+
+ public:
+ // FIXME: takes begin, end, and initializes with the reverse
+ AssociativeMap():
+ container(), Parent(container) {}
+
+ private:
+ UniquePairAssociativeContainer_ container;
+};
+
+
+/* Dereference Map */
+template<class Iterator_>
+class DereferenceMap
+{
+ public:
+ typedef Iterator_ Iterator;
+ typedef Iterator key_type;
+ typedef typename boost::iterator_value<Iterator>::type value_type;
+ typedef boost::readable_property_map_tag category;
+
+ public:
+ value_type operator[](const key_type& k) const { return *k; }
+};
+
+
+/* Binary Search Map */
+template<class Query_, class Index_, class Comparison_ = std::less<Query_> >
+class BinarySearchMap
+{
+ public:
+ typedef Query_ Query;
+ typedef Index_ Index;
+ typedef Comparison_ Comparison;
+
+ typedef Query key_type;
+ typedef Index value_type;
+ typedef boost::readable_property_map_tag category;
+
+ public:
+ BinarySearchMap(Index bg, Index end,
+ Comparison cmp = Comparison()):
+ bg_(bg), end_(end), cmp_(cmp) {}
+
+ value_type operator[](const key_type& k) const
+ {
+ value_type res = std::lower_bound(bg_, end_, k, cmp_);
+ AssertMsg(!cmp_(*res, k) && !cmp_(k, *res), "Query must always be found");
+ return res;
+ }
+
+ private:
+ Index bg_;
+ Index end_;
+ Comparison cmp_;
+
+};
+
+/* Offset Map */
+template<class From_, class To_>
+struct OffsetMap
+{
+ typedef From_ From;
+ typedef To_ To;
+ typedef From key_type;
+ typedef To value_type;
+
+ OffsetMap(From bg_from, To bg_to):
+ bg_from_(bg_from), bg_to_(bg_to) {}
+
+ To operator[](From i) const { return bg_to_ + (i - bg_from_); }
+
+ From from() const { return bg_from_; }
+ To to() const { return bg_to_; }
+
+
+ template<class NewFrom_> struct rebind_from
+ { typedef OffsetMap<NewFrom_, To_> other; };
+ template<class NewTo_> struct rebind_to
+ { typedef OffsetMap<From_, NewTo_> other; };
+
+
+ private:
+ From bg_from_;
+ To bg_to_;
+};
+
+template<class From_, class To_>
+OffsetMap<From_, To_>
+make_offset_map(From_ bg_from, To_ bg_to)
+{ return OffsetMap<From_, To_>(bg_from, bg_to); }
+
+
+template<class From_, class To_, class FromIndex_, class ToIndex_>
+struct OffsetBeginMap
+{
+ typedef From_ From;
+ typedef To_ To;
+ typedef FromIndex_ key_type;
+ typedef ToIndex_ value_type;
+
+ OffsetBeginMap(const From& from, const To& to):
+ from_(from), to_(to) {}
+
+ value_type operator[](key_type i) const { return to_.begin() + (i - from_.begin()); }
+
+ const From& from() const { return from_; }
+ const To& to() const { return to_; }
+
+ private:
+ const From& from_;
+ const To& to_;
+};
+
+
+/* ChainMap */
+template<class Map1, class Map2>
+class ChainMap
+{
+ public:
+ typedef typename Map1::key_type key_type;
+ typedef typename Map2::value_type value_type;
+
+
+ ChainMap(const Map1& m1, const Map2& m2):
+ m1_(m1), m2_(m2) {}
+ value_type operator[](const key_type& k) const { return m2_[m1_[k]]; }
+
+ private:
+ const Map1& m1_;
+ const Map2& m2_;
+};
+
+/* ThroughMap */
+template<class Functor_, class Map_>
+class ThroughMap
+{
+ public:
+ typedef Map_ Map;
+ typedef Functor_ Functor;
+
+ typedef typename Functor::result_type result_type;
+ typedef typename Map_::key_type first_argument_type;
+
+ ThroughMap(const Map& map,
+ const Functor& functor):
+ map_(map),
+ functor_(functor) {}
+
+ result_type operator()(first_argument_type a) const { return functor_(map_[a]); }
+
+ private:
+ const Map& map_;
+ const Functor& functor_;
+};
+
+template<class Map, class Functor>
+ThroughMap<Functor, Map>
+evaluate_through_map(const Map& map, const Functor& functor)
+{ return ThroughMap<Functor, Map>(map, functor); }
+
+#endif // __PROPERTY_MAPS_H__
--- a/include/utilities/sys.h Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-// sys.h
-//
-// This header file is included at the top of every source file,
-// before any other header file.
-// It is intended to add defines that are needed globally and
-// to work around Operating System dependend incompatibilities.
-
-// EXAMPLE: If you use autoconf you can add the following here.
-// #ifdef HAVE_CONFIG_H
-// #include "config.h"
-// #endif
-
-// EXAMPLE: You could add stuff like this here too
-// (Otherwise add -DCWDEBUG to your CFLAGS).
-// #if defined(WANTSDEBUGGING) && defined(HAVE_LIBCWD_BLAHBLAH)
-// #define CWDEBUG
-// #endif
-
-// The following is the libcwd related mandatory part.
-// It must be included before any system header file is included!
-#ifdef CWDEBUG
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#include <libcwd/sys.h>
-#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/utilities/timer.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,69 @@
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+// Adapted with minor changes from http://oldmill.uchicago.edu/~wilder/Code/timer/
+
+#include <ctime>
+#include <iostream>
+#include <iomanip>
+
+class Timer
+{
+ public:
+ Timer(): start_time(0), acc_time(0) {}
+
+ void start();
+ void stop();
+ void check(const char* msg = 0) const;
+
+ private:
+ clock_t start_clock;
+ time_t start_time;
+ double acc_time;
+
+ double elapsed_time() const;
+};
+
+// Return the total time that the timer has been in the "running" state since
+// it was last "started". For "short" time periods (less than an hour), the
+// actual cpu time used is reported instead of the elapsed time.
+inline double
+Timer::
+elapsed_time() const
+{
+ time_t acc_sec = time(0) - start_time;
+ if (acc_sec < 3600)
+ return (clock() - start_clock) / (1.0 * CLOCKS_PER_SEC);
+ else
+ return (1.0 * acc_sec);
+}
+
+inline void
+Timer::
+start()
+{
+ start_clock = clock();
+ start_time = time(0);
+}
+
+inline void
+Timer::
+stop()
+{
+ acc_time += elapsed_time();
+}
+
+// Print out an optional message followed by the current timer timing.
+inline void
+Timer::
+check(const char* msg) const
+{
+ // Print an optional message, something like "Checking timer t";
+ if (msg) std::cout << msg << " : ";
+
+ std::cout << "Elapsed time [" << std::setiosflags(std::ios::fixed)
+ << std::setprecision(2) << acc_time << "] seconds\n";
+}
+
+#endif // __TIMER_H__
+
--- a/include/utilities/types.h Fri Aug 24 16:58:25 2007 -0400
+++ b/include/utilities/types.h Tue Jun 27 09:37:05 2017 -0700
@@ -2,10 +2,11 @@
#define __TYPES_H__
#include <limits>
+#include <iostream>
/* Types */
typedef bool Sign;
-typedef short int Dimension;
+typedef short int Dimension;
const Sign POS = true;
const Sign NEG = false;
typedef double RealType;
@@ -15,4 +16,48 @@
typedef const unsigned int& version_type;
+// Empty is made a template so that we don't have to compile and deal with a library
+// solely for its operator<<(out, e) function
+template<typename T = void>
+struct Empty {};
+
+
+struct use_default {};
+
+template<class T, class Default>
+struct if_default
+{ typedef T type; };
+
+template<class Default>
+struct if_default<use_default, Default>
+{ typedef Default type; };
+
+
+template<typename T>
+std::ostream& operator<<(std::ostream& out, Empty<T> e) { return out; }
+
+enum SwitchType
+{
+ DiffDim = 0,
+ Case1 = 0x4,
+ Case12 = 0x5,
+ Case112 = 0x6,
+ Case2 = 0x8,
+ Case212 = 0x9,
+ Case3 = 0x10,
+ Case31 = 0x11,
+ Case4 = 0x20,
+};
+
+// Nothing to do for serializing Empty, but still need to provide this function
+namespace boost {
+namespace serialization {
+
+template<class Archive, class T>
+void serialize(Archive & ar, Empty<T>&, const unsigned int )
+{}
+
+} // namespace serialization
+} // namespace boost
+
#endif // __TYPES_H__
--- a/src/debug.cpp Fri Aug 24 16:58:25 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-#include "sys.h"
-#include "debug.h"
-
-#ifdef CWDEBUG
-
-namespace dionysus
-{
- namespace debug
- {
- namespace channels // DEBUGCHANNELS is set to this in debug.h
- {
- namespace dc
- {
- // Add new debug channels here.
- channel_ct filtration("FILTRATION");
- channel_ct transpositions("TRANSPOS");
- channel_ct vineyard("VINEYARD");
- channel_ct cycle("CYCLE");
- channel_ct lsfiltration("LSFILTRATION");
- channel_ct orderlist("ORDERLIST");
- }
- } // namespace DEBUGCHANNELS
-
- // Initialize debugging code from new threads.
- void init_thread(void)
- {
- // Everything below needs to be repeated at the start of every
- // thread function, because every thread starts in a completely
- // reset state with all debug channels off etc.
-
- #if LIBCWD_THREAD_SAFE // For the non-threaded case this is set by the rcfile.
- // Turn on all debug channels by default.
- ForAllDebugChannels(while(!debugChannel.is_on()) debugChannel.on());
- // Turn off specific debug channels.
- Debug(dc::bfd.off());
- Debug(dc::malloc.off());
- #endif
-
- // Turn on debug output.
- // Only turn on debug output when the environment variable SUPPRESS_DEBUG_OUTPUT is not set.
- Debug(if (getenv("SUPPRESS_DEBUG_OUTPUT") == NULL) libcw_do.on());
- #if LIBCWD_THREAD_SAFE
- Debug(libcw_do.set_ostream(&std::cout, &cout_mutex));
-
- // Set the thread id in the margin.
- char margin[12];
- sprintf(margin, "%-10lu ", pthread_self());
- Debug(libcw_do.margin().assign(margin, 11));
- #else
- Debug(libcw_do.set_ostream(&std::cout));
- #endif
-
- // Write a list of all existing debug channels to the default debug device.
- Debug(list_channels_on(libcw_do));
- }
-
- // Initialize debugging code from main().
- void init(void)
- {
- // You want this, unless you mix streams output with C output.
- // Read http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#8 for an explanation.
- // We can't use it, because other code uses printf to write to the console.
- //std::ios::sync_with_stdio(false);
-
- // This will warn you when you are using header files that do not belong to the
- // shared libcwd object that you linked with.
- Debug(check_configuration());
-
- #if CWDEBUG_ALLOC
- // Remove all current (pre- main) allocations from the Allocated Memory Overview.
- libcwd::make_all_allocations_invisible_except(NULL);
- #endif
-
- //Debug(read_rcfile());
-
- init_thread();
- }
- } // namespace debug
-} // namespace dionysus
-
-#endif // CWDEBUG
--- a/tests/geometry/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/tests/geometry/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,12 +1,17 @@
set (targets
euclidean
- polynomial
- test-eventqueue
- test-kinetic-sort
- test-linalg)
+ test-ksort-linear
+ test-eventqueue)
+
+if (use_synaps)
+ set (targets ${targets}
+ polynomial
+ test-kinetic-sort
+ test-linalg)
+endif (use_synaps)
foreach (t ${targets})
add_executable (${t} ${t}.cpp)
- target_link_libraries (${t} ${synaps_libraries})
+ target_link_libraries (${t} ${synaps_libraries} ${libraries})
endforeach (t ${targets})
--- a/tests/geometry/polynomial.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/tests/geometry/polynomial.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -7,7 +7,7 @@
typedef UPolynomial<ZZ> PolynomialKernel;
typedef PolynomialKernel::Polynomial Polynomial;
-typedef PolynomialKernel::RationalFunction RationalF;
+typedef PolynomialKernel::Function RationalF;
typedef Kernel<RationalF> K;
typedef K::Point Point;
@@ -75,8 +75,6 @@
while (!roots.empty()) { std::cout << roots.top() << std::endl; roots.pop(); }
}
- return 0;
-
// Edges
{
PointContainer vertices(2);
--- a/tests/geometry/test-eventqueue.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/tests/geometry/test-eventqueue.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -4,20 +4,30 @@
int main()
{
- typedef EventQueue<int, std::less<int> > EQ;
+ typedef EventQueue<int, std::less<int> > EQ;
typedef EQ::iterator iterator;
EQ queue;
- iterator i = queue.push(4);
- queue.push(2);
- queue.push(7);
- iterator j = queue.push(6);
- queue.push(5);
+ std::cout << "Queue initialized" << std::endl;
+
+ iterator i1 = queue.push(4);
+ iterator i2 = queue.push(2);
+ iterator i3 = queue.push(9);
+ iterator i4 = queue.push(6);
+ iterator i5 = queue.push(5);
- *i = 8;
- queue.update(i);
- queue.remove(j);
+ std::cout << "Values inserted" << std::endl;
+ queue.print(std::cout, " ");
+
+ queue.replace(i1,1);
+ queue.remove(i4);
+ queue.replace(i5,10);
+
+ *i3 = 14;
+ queue.demoted(i3);
+
+ std::cout << "Replaced and removed" << std::endl;
while (!queue.empty())
{
--- a/tests/geometry/test-kinetic-sort.cpp Fri Aug 24 16:58:25 2007 -0400
+++ b/tests/geometry/test-kinetic-sort.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -6,6 +6,8 @@
#include <boost/utility.hpp>
#include <boost/bind.hpp>
+#include <utilities/log.h>
+
typedef double FieldType;
//typedef ZZ FieldType;
//typedef QQ FieldType;
@@ -37,14 +39,23 @@
s->splice(i, *s, boost::next(i));
}
-int main()
+int main(int argc, char** argv)
{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("geometry/simulator") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("geometry/kinetic-sort") );
+#endif
+
SimulatorFT simulator;
SortDS list;
// Insert polynomials and sort the list for current time
list.push_back(Polynomial("x^3 - 3"));
list.push_back(Polynomial("x^2 - 2*x - 2"));
+ list.push_back(Polynomial("x^2 - 2*x - 2"));
list.push_back(Polynomial("2*x - 4"));
list.push_back(Polynomial("x"));
list.push_back(Polynomial("-x + 4"));
@@ -59,12 +70,21 @@
// Setup kinetic sort
KineticSortDS ks(list.begin(), list.end(), boost::bind(swap, &list, _1, _2), &simulator);
- while(!simulator.reached_infinity() && simulator.current_time() < 4)
+ std::cout << "Examining " << simulator;
+
+ while(!simulator.reached_infinity())
{
std::cout << "Current time before: " << simulator.current_time() << std::endl;
//if (!ks.audit(&simulator)) return 1;
- //simulator.print(std::cout << "Auditing ");
+ ks.audit(&simulator);
+ std::cout << "Examining " << simulator;
simulator.process();
std::cout << "Current time after: " << simulator.current_time() << std::endl;
}
+ ks.audit(&simulator);
+ std::cout << "Examining " << simulator;
+
+ std::cout << "Done at " << simulator.current_time() << std::endl;
+ for (SortDS::const_iterator cur = list.begin(); cur != list.end(); ++cur)
+ std::cout << " " << *cur << std::endl;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/geometry/test-ksort-linear.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,88 @@
+#include <geometry/simulator.h>
+#include <geometry/kinetic-sort.h>
+#include <geometry/linear-kernel.h>
+#include <iostream>
+
+#include <boost/utility.hpp>
+#include <boost/bind.hpp>
+
+#include <utilities/log.h>
+
+typedef double FieldType;
+//typedef ZZ FieldType;
+//typedef QQ FieldType;
+typedef LinearKernel<FieldType> LKernel;
+typedef LKernel::Function Function;
+typedef std::list<Function> SortDS;
+typedef SortDS::iterator SortDSIterator;
+typedef Simulator<LKernel> SimulatorFT;
+
+class TrajectoryExtractor
+{
+ public:
+ Function operator()(SortDSIterator i) const { return *i; }
+};
+
+typedef KineticSort<SortDSIterator, TrajectoryExtractor, SimulatorFT> KineticSortDS;
+
+struct EvaluatedComparison: public std::binary_function<const Function&, const Function&, bool>
+{
+ EvaluatedComparison(FieldType v): vv(v) {}
+ bool operator()(const Function& p1, const Function& p2)
+ { return p1(vv) < p2(vv); }
+ FieldType vv;
+};
+
+void swap(SortDS* s, SortDSIterator i, SimulatorFT* simulator)
+{
+ std::cout << "Swapping " << *i << " " << *boost::next(i) << std::endl;
+ s->splice(i, *s, boost::next(i));
+}
+
+int main(int argc, char** argv)
+{
+#ifdef LOGGING
+ rlog::RLogInit(argc, argv);
+
+ stderrLog.subscribeTo( RLOG_CHANNEL("error") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("geometry/simulator") );
+ stdoutLog.subscribeTo( RLOG_CHANNEL("geometry/kinetic-sort") );
+#endif
+
+ SimulatorFT simulator;
+ SortDS list;
+
+ // Insert polynomials and sort the list for current time
+ list.push_back(Function(2, -2));
+ list.push_back(Function(1, 3));
+ list.push_back(Function(-1, 6));
+ list.push_back(Function(2));
+ list.push_back(Function(2, 2));
+ //list.sort(EvaluatedComparison(simulator.current_time()));
+ list.sort(EvaluatedComparison(0));
+
+ // Print out the list
+ for (SortDS::const_iterator cur = list.begin(); cur != list.end(); ++cur)
+ std::cout << *cur << std::endl;
+
+ // Setup kinetic sort
+ KineticSortDS ks(list.begin(), list.end(), boost::bind(swap, &list, _1, _2), &simulator);
+
+ std::cout << "Examining " << simulator;
+
+ while(!simulator.reached_infinity())
+ {
+ std::cout << "Current time before: " << simulator.current_time() << std::endl;
+ //if (!ks.audit(&simulator)) return 1;
+ ks.audit(&simulator);
+ std::cout << "Examining " << simulator;
+ simulator.process();
+ std::cout << "Current time after: " << simulator.current_time() << std::endl;
+ }
+ ks.audit(&simulator);
+ std::cout << "Examining " << simulator;
+
+ std::cout << "Done at " << simulator.current_time() << std::endl;
+ for (SortDS::const_iterator cur = list.begin(); cur != list.end(); ++cur)
+ std::cout << " " << *cur << std::endl;
+}
--- a/tests/utilities/CMakeLists.txt Fri Aug 24 16:58:25 2007 -0400
+++ b/tests/utilities/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -1,8 +1,13 @@
set (targets
+ test-set-iterators
test-consistencylist
test-orderlist)
+if (counters)
+ set (targets ${targets} test-counters)
+endif (counters)
+
foreach (t ${targets})
- add_executable (${t} ${t}.cpp ${external_sources})
+ add_executable (${t} ${t}.cpp)
target_link_libraries (${t} ${libraries})
endforeach (t ${targets})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/utilities/test-counters.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,33 @@
+#include <utilities/counter.h>
+
+static Counter* cTestElaborate = GetCounter("test/elaborate");
+static Counter* cTestBasic = GetCounter("test/basic");
+static Counter* cTestBasicSub = GetCounter("test/basic/sub");
+
+int main()
+{
+ SetFrequency(cTestBasic, 2);
+
+ Count(cTestBasic);
+ Count(cTestBasicSub);
+ Count(cTestBasicSub);
+ Count(cTestBasicSub);
+ Count(cTestElaborate);
+ Count(cTestBasic);
+ Count(cTestElaborate);
+ Count(cTestBasic);
+ Count(cTestBasic);
+ CountNum(cTestBasic, 25);
+ CountNum(cTestBasic, 132);
+ CountNum(cTestBasic, 25);
+ CountNum(cTestBasic, 121);
+ CountNum(cTestBasic, 132);
+ CountNum(cTestBasic, 25);
+
+ SetTrigger(cTestBasic, &rootCounter);
+ Count(cTestBasic);
+ Count(cTestBasic);
+
+ SetFrequency(cTestElaborate, 3);
+ Count(cTestElaborate);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/utilities/test-set-iterators.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,59 @@
+#include <vector>
+#include <list>
+#include <algorithm>
+#include <iostream>
+
+#include <utilities/indirect.h>
+
+#include <boost/lambda/lambda.hpp>
+using boost::lambda::_1;
+
+int main()
+{
+ std::vector<int> v;
+ v.push_back(1);
+ v.push_back(3);
+ v.push_back(5);
+ v.push_back(7);
+ v.push_back(9);
+ std::cout << "v: ";
+ std::for_each(v.begin(), v.end(), std::cout << _1 << ' ');
+ std::cout << std::endl;
+
+ std::list<int> l;
+ l.push_back(2);
+ l.push_back(3);
+ l.push_back(4);
+ l.push_back(5);
+ l.push_back(6);
+ l.push_back(8);
+ std::cout << "l: ";
+ std::for_each(l.begin(), l.end(), std::cout << _1 << ' ');
+ std::cout << std::endl;
+ std::cout << std::endl;
+
+ std::cout << "v \\cap l: ";
+ std::for_each(make_intersection_iterator(v.begin(), v.end(), l.begin(), l.end(), std::less<int>()),
+ make_intersection_iterator(v.end(), v.end(), l.end(), l.end(), std::less<int>()),
+ std::cout << _1 << ' ');
+ std::cout << std::endl;
+
+ std::cout << "v - l: ";
+ std::for_each(make_difference_iterator(v.begin(), v.end(), l.begin(), l.end(), std::less<int>()),
+ make_difference_iterator(v.end(), v.end(), l.end(), l.end(), std::less<int>()),
+ std::cout << _1 << ' ');
+ std::cout << std::endl;
+ std::cout << std::endl;
+
+ std::cout << "l \\cap v: ";
+ std::for_each(make_intersection_iterator(l.begin(), l.end(), v.begin(), v.end(), std::less<int>()),
+ make_intersection_iterator(l.end(), l.end(), v.end(), v.end(), std::less<int>()),
+ std::cout << _1 << ' ');
+ std::cout << std::endl;
+
+ std::cout << "l - v: ";
+ std::for_each(make_difference_iterator(l.begin(), l.end(), v.begin(), v.end(), std::less<int>()),
+ make_difference_iterator(l.end(), l.end(), v.end(), v.end(), std::less<int>()),
+ std::cout << _1 << ' ');
+ std::cout << std::endl;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,19 @@
+#find_package (Qt4)
+
+#if (QT4_FOUND)
+# set (QT_USE_QTOPENGL TRUE)
+# set (QT_USE_QTXML TRUE)
+# include (${QT_USE_FILE})
+
+# add_subdirectory (diagram-viewer)
+##find_library (gle_LIBRARY NAMES gle)
+##find_library (QGLViewer_LIBRARY NAMES QGLViewer)
+##find_path (QGLViewer_INCLUDE_DIR QGLViewer/qglviewer.h)
+##include_directories (${QGLViewer_INCLUDE_DIR})
+#endif (QT4_FOUND)
+
+
+add_executable (extract-diagram extract-diagram.cpp)
+target_link_libraries (extract-diagram ${libraries} ${Boost_SERIALIZATION_LIBRARY})
+
+add_subdirectory (matching)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/diagram-viewer/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,18 @@
+set (diagram-viewerSources
+ diagram.cpp
+ diagram-viewer-main.cpp)
+
+set (diagram-viewerHeaders
+ diagram.h)
+
+qt4_wrap_cpp (diagram-viewerMocSources ${diagram-viewerHeaders})
+
+set (libraries ${libraries}
+ ${Boost_SERIALIZATION_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${QT_LIBRARIES})
+
+add_executable (diagram-viewer ${diagram-viewerSources}
+ ${diagram-viewerMocSources})
+
+target_link_libraries (diagram-viewer ${libraries})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/diagram-viewer/diagram-viewer-main.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,59 @@
+#include <qapplication.h>
+#include <QtGui>
+
+#include "diagram.h"
+
+#include <fstream>
+#include <map>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/serialization/map.hpp>
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+
+
+int main (int argc, char *argv[])
+{
+ std::string diagrams_filename;
+ int dimension;
+
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("diagrams-file", po::value<std::string>(&diagrams_filename), "The collection of persistence diagrams")
+ ("dimension", po::value<int>(&dimension), "Dimension of the diagram to show");
+
+ po::positional_options_description p;
+ p.add("diagrams-file", 1);
+ p.add("dimension", 2);
+
+ po::options_description all; all.add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+ if (!vm.count("diagrams-file") || !vm.count("dimension"))
+ {
+ std::cout << "Usage: " << argv[0] << " diagrams-file dimension" << std::endl;
+ std::cout << hidden << std::endl;
+ return 1;
+ }
+
+ std::map<Dimension, PDiagram> dgms;
+ std::ifstream ifs(diagrams_filename.c_str());
+ boost::archive::binary_iarchive ia(ifs);
+ ia >> dgms;
+
+
+ QApplication application(argc, argv);
+
+ std::cout << dimension << std::endl;
+ std::cout << dgms[dimension] << std::endl;
+
+ DgmViewer pd(dgms[dimension]);
+ pd.show();
+
+ // Run main loop.
+ return application.exec();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/diagram-viewer/diagram.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,95 @@
+#include <iostream>
+
+#include <QtGui>
+#include <QRectF>
+
+#include "diagram.h"
+#include <cmath>
+
+//static const double ellipse_size = 0.035;
+static const double ellipse_size = 3;
+
+/* DgmViewer Implementation */
+DgmViewer::DgmViewer(const PDiagram& dgm):
+ min_x(0), min_y(0), max_x(0), max_y(0)
+{
+ points.reserve(dgm.size());
+
+ for (PDiagram::const_iterator cur = dgm.begin(); cur != dgm.end(); ++cur)
+ {
+ min_x = std::min(min_x, cur->x());
+ min_y = std::min(min_y, cur->y());
+ max_x = std::max(max_x, cur->x());
+ max_y = std::max(max_y, cur->y());
+
+ points.push_back(new DgmPoint(*cur, ellipse_size));
+ }
+
+ addDgmPoints();
+ setWindowTitle(QString("Persistence Diagram"));
+}
+DgmViewer::~DgmViewer()
+{
+ for (PointsVector::iterator cur = points.begin(); cur != points.end(); ++cur)
+ delete *cur;
+}
+
+
+void DgmViewer::addDgmPoints()
+{
+ RealType min = std::min(min_x, min_y);
+ RealType max = std::max(max_x, max_y);
+
+ QGraphicsLineItem* diagonal = new QGraphicsLineItem(QLineF(min, -min, max, -max));
+ QGraphicsLineItem* y_axis = new QGraphicsLineItem(QLineF(0, -min_y, 0, -max_y));
+ QGraphicsLineItem* x_axis = new QGraphicsLineItem(QLineF(min_x, 0, max_x, 0));
+
+ scene.addItem(diagonal);
+ scene.addItem(y_axis);
+ scene.addItem(x_axis);
+
+ for (PointsVector::const_iterator cur = points.begin(); cur != points.end(); ++cur)
+ scene.addItem(*cur);
+
+ //scale(100,100);
+ setScene(&scene);
+ setRenderHint(QPainter::Antialiasing);
+ ensureVisible(scene.itemsBoundingRect());
+ //setMinimumSize( (int)(maxX - minX)*100 + 100, (int) (maxY - minY)*100 + 100);
+}
+
+
+DgmPoint::DgmPoint(QGraphicsItem* parent):
+ QGraphicsItem(parent)
+{
+}
+
+DgmPoint::DgmPoint(const Parent& pt, qreal size, QGraphicsItem *parent):
+ Parent(pt), ellipse_size(size), QGraphicsItem(parent)
+{
+ setToolTip(QString("(%1, %2)").arg(getX()).arg(getY()));
+}
+
+DgmPoint::DgmPoint(RealType b, RealType d, qreal size, QGraphicsItem *parent):
+ Parent(b, d), ellipse_size(size), QGraphicsItem(parent)
+{
+ setToolTip(QString("(%1, %2)").arg(getX()).arg(getY()));
+}
+
+void DgmPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ //QBrush solidFill(unselectColor);
+ //QBRush selectSolidFill(selectColor);
+ painter->setBrush(Qt::SolidPattern);
+ //painter->setPen(selectColor);
+ painter->drawEllipse(QRectF(getX() - ellipse_size, -getY() - ellipse_size, 2*ellipse_size, 2*ellipse_size));
+}
+
+
+QRectF DgmPoint::boundingRect() const
+{
+ return QRectF(getX() - ellipse_size, -getY() - ellipse_size, 2*ellipse_size, 2*ellipse_size);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/diagram-viewer/diagram.h Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,60 @@
+#ifndef __DIAGRAM_H__
+#define __DIAGRAM_H__
+
+#include <QtGui>
+#include <QObject>
+#include <QColor>
+
+#include <map>
+
+#include <utilities/types.h>
+#include <topology/persistence-diagram.h>
+
+typedef PersistenceDiagram<> PDiagram;
+typedef std::map<Dimension, PDiagram> Diagrams;
+
+class DgmPoint;
+
+class DgmViewer: public QGraphicsView
+{
+ Q_OBJECT
+
+ public:
+ typedef std::vector<DgmPoint*> PointsVector;
+
+ DgmViewer(const PDiagram& dgm);
+ ~DgmViewer();
+
+ void addDgmPoints();
+
+ private:
+ PointsVector points;
+ QGraphicsScene scene;
+ RealType min_x, min_y, max_x, max_y;
+};
+
+
+class DgmPoint: public PDPoint<>, public QGraphicsItem
+{
+ public:
+ typedef PDPoint<> Parent;
+
+ DgmPoint(QGraphicsItem* parent = 0);
+ DgmPoint(const Parent& pt, qreal size, QGraphicsItem *parent = 0);
+ DgmPoint(RealType b, RealType d, qreal size, QGraphicsItem *parent = 0);
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ QRectF boundingRect() const;
+
+ qreal getX() const { return Parent::x(); }
+ qreal getY() const { return Parent::y(); }
+
+ int type() const { return QGraphicsItem::UserType + 1; }
+
+ private:
+ // size of rectangle containing ellipses
+ qreal ellipse_size;
+};
+
+
+#endif // __DIAGRAM_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/draw-diagram/draw.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,25 @@
+#!/usr/bin/env python2
+
+import pd
+from sys import argv, exit
+
+if len(argv) < 2:
+ print "Usage: %s FILENAME [MULTIPLIER=1] [NOISE=0] [RADIUS=.15] [DIMENSIONS=XMIN,YMIN,XMAX,YMAX]" % argv[0]
+ print " MULTIPLIER - multiply coordinates of each point by this quantity"
+ print " NOISE - filter out points below this persistence"
+ print " RADIUS - radius of a point in the persistence diagram"
+ print " DIMENSIONS - dimensions of the persistence diagram"
+ print
+ print " Example: %s torus.dgm 1 0 .05 -1,-1,10,10" % argv[0]
+ exit()
+
+multiplier = float(argv[2]) if len(argv) > 2 else 1
+noise = float(argv[3]) if len(argv) > 3 else 0
+R = float(argv[4]) if len(argv) > 4 else .15
+dimensions = map(float, argv[5].split(',')) if len(argv) > 5 else None
+
+noise_filter = pd.noise_filter(noise)
+amplify_filter = pd.amplify_filter(multiplier)
+
+dgm = pd.PersistenceDiagram(argv[1], lambda x,y: noise_filter(x,y) and amplify_filter(x,y))
+dgm.savePDF(argv[1] + '.', radius = R, dimensions = dimensions)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/draw-diagram/pd.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,113 @@
+#!/usr/bin/env python2
+
+# Author: Dmitriy Morozov <morozov@cs.duke.edu>
+# (2004-2008) Department of Computer Science, Duke University
+
+import pyx, string, sys, math
+
+class PersistenceDiagram:
+ def drawAxes(self,c,radius,spacing = 1, dimensions = None):
+ if not dimensions:
+ xmax = math.ceil(max(self.xmax,1) + radius + 1)
+ xmin = math.floor(min(self.xmin,-1) - radius - 1)
+ ymax = math.ceil(max(self.ymax,1) + radius + 1)
+ ymin = math.floor(min(self.ymin,-1) - radius - 1)
+ else:
+ xmin,ymin,xmax,ymax = dimensions
+
+ minmax = min([xmax, ymax])
+ maxmin = max([xmin, ymin])
+ for i in xrange(int(math.floor(xmin)),int(math.ceil(xmax)), spacing):
+ c.stroke(pyx.path.line(i,ymin,i,ymax), [pyx.style.linestyle.dashed, pyx.color.gray(0.5)])
+ for j in xrange(int(math.floor(ymin)),int(math.ceil(ymax)), spacing):
+ c.stroke(pyx.path.line(xmin,j,xmax,j), [pyx.style.linestyle.dashed, pyx.color.gray(0.5)])
+ c.stroke(pyx.path.line(xmin,0,xmax,0), [pyx.deco.earrow.normal])
+ c.stroke(pyx.path.line(0,ymin,0,ymax), [pyx.deco.earrow.normal])
+ c.stroke(pyx.path.line(maxmin,maxmin,minmax,minmax))
+
+
+ def drawCanvas(self, c, points, color = 'red', filled = 1, radius = 0.15):
+ for p in points:
+ self.drawPoint(c,p,color,filled,radius)
+
+ def drawPoint(self,c,p, color = 'red', filled = 1, radius = 0.15):
+ if color == 'red':
+ options = [pyx.color.rgb.red]
+ elif color == 'blue':
+ options = [pyx.color.rgb.blue]
+ elif color == 'green':
+ options = [pyx.color.rgb.green]
+ else:
+ options = []
+
+ if filled:
+ draw = c.fill
+ else:
+ draw = c.stroke
+
+ xmax = max(self.xmax,1)
+ xmin = min(self.xmin,-1)
+ ymax = max(self.ymax,1)
+ ymin = min(self.ymin,-1)
+
+ x,y = p
+ if abs(x) == float('inf') or abs(y) == float('inf'): radius = radius * 2
+ if x == float('inf'): x = xmax + 1
+ if x == float('-inf'): x = xmin - 1
+ if y == float('inf'): y = ymax + 1
+ if y == float('-inf'): y = ymin -1
+
+ draw(pyx.path.circle(x,y,radius), options)
+
+ def savePDF(self, filename, color = 'red', filled = 1, radius = 0.15, axes = 1, dimensions = None):
+ for d in self.points.keys():
+ c = pyx.canvas.canvas()
+ self.drawAxes(c, radius, axes, dimensions)
+ self.drawCanvas(c, self.points[d], color, filled, radius)
+ c.writePDFfile(filename + str(d))
+
+ def add(self, d, p, filter):
+ x,y = p
+ p = filter(x,y)
+ if not p: return
+ x,y = p
+
+ if d not in self.points.keys():
+ self.points[d] = []
+ self.points[d] += [(x,y)]
+
+ if abs(x) != float('inf'):
+ self.xmax = max(x,self.xmax)
+ self.xmin = min(x,self.xmin)
+ if abs(y) != float('inf'):
+ self.ymax = max(y,self.ymax)
+ self.ymin = min(y,self.ymin)
+
+ def load(self,filename, filter):
+ self.xmax = self.ymax = 0
+ self.xmin = self.ymin = 0
+ f = file(filename, 'r')
+ for line in f:
+ if line.strip().startswith('#'): continue
+ dim,xstr,ystr = string.split(line)[:3]
+ self.add(dim, (float(xstr),float(ystr)), filter)
+
+ def __init__(self, filename, filter = lambda x,y: (x,y)):
+ self.points = {}
+ self.load(filename, filter)
+
+
+def noise_filter(epsilon):
+ def noise(x,y):
+ if y - x <= epsilon: return None
+ return (x,y)
+
+ return noise
+
+def amplify_filter(x_mult, y_mult = None):
+ if not y_mult: y_mult = x_mult
+
+ def amplify(x,y):
+ return (x_mult*x, y_mult*y)
+
+ return amplify
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/extract-diagram.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,30 @@
+#include <topology/persistence-diagram.h>
+
+#include <string>
+#include <map>
+#include <iostream>
+
+#include <fstream>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/serialization/map.hpp>
+
+typedef PersistenceDiagram<> PDgm;
+
+int main(int argc, char** argv)
+{
+ if (argc < 2)
+ {
+ std::cout << "Usage: " << argv[0] << " FILENAME" << std::endl;
+ return 0;
+ }
+ std::string infilename = argv[1];
+
+ std::ifstream ifs(infilename.c_str());
+ boost::archive::binary_iarchive ia(ifs);
+
+ std::map<Dimension, PDgm> dgms;
+ ia >> dgms;
+ for (std::map<Dimension, PDgm>::const_iterator cur = dgms.begin(); cur != dgms.end(); ++cur)
+ for (PDgm::const_iterator pcur = cur->second.begin(); pcur != cur->second.end(); ++pcur)
+ std::cout << cur->first << " " << pcur->x() << " " << pcur->y() << std::endl;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/matching/CMakeLists.txt Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,9 @@
+set (libraries ${libraries}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY})
+
+# Build compare-diagrams
+add_executable (bottleneck bottleneck.cpp)
+target_link_libraries (bottleneck ${libraries})
+
+add_executable (wasserstein wasserstein.cpp)
+target_link_libraries (wasserstein ${libraries})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/matching/bottleneck.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,66 @@
+#include <iostream>
+#include <fstream>
+
+#include <topology/persistence-diagram.h>
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+
+typedef PersistenceDiagram<> PDgm;
+typedef PDgm::Point Point;
+
+
+void read_diagram(const std::string& filename, PDgm& dgm);
+void process_program_options(int argc, char* argv[], std::string& filename1, std::string& filename2);
+
+
+int main(int argc, char* argv[])
+{
+ std::string filename1, filename2;
+ process_program_options(argc, argv, filename1, filename2);
+
+ PDgm dgm1, dgm2;
+ read_diagram(filename1, dgm1);
+ read_diagram(filename2, dgm2);
+
+ std::cout << bottleneck_distance(dgm1, dgm2) << std::endl;
+}
+
+
+void read_diagram(const std::string& filename, PDgm& dgm)
+{
+ std::ifstream in(filename.c_str());
+ double birth, death;
+ while(in)
+ {
+ in >> birth >> death;
+ //std::cout << "birth: " << birth << ", death: " << death << std::endl;
+ if (in)
+ dgm.push_back(Point(birth, death));
+ }
+}
+
+void process_program_options(int argc, char* argv[], std::string& filename1, std::string& filename2)
+{
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file1", po::value<std::string>(&filename1), "The first collection of persistence diagrams")
+ ("input-file2", po::value<std::string>(&filename2), "The second collection of persistence diagrams");
+
+ po::positional_options_description p;
+ p.add("input-file1", 1);
+ p.add("input-file2", 2);
+
+ po::options_description all; all.add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+ if (!vm.count("input-file1") || !vm.count("input-file2"))
+ {
+ std::cout << "Usage: " << argv[0] << " input-file1 input-file2" << std::endl;
+ std::abort();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/matching/wasserstein.cpp Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,74 @@
+#include <iostream>
+#include <fstream>
+
+#include <topology/persistence-diagram.h>
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+
+typedef PersistenceDiagram<> PDgm;
+typedef PDgm::Point Point;
+
+
+void read_diagram(const std::string& filename, PDgm& dgm);
+void process_program_options(int argc, char* argv[], std::string& filename1, std::string& filename2, double& wasserPower);
+
+
+int main(int argc, char* argv[])
+{
+ std::string filename1, filename2;
+ double wasserPower;
+ process_program_options(argc, argv, filename1, filename2, wasserPower);
+
+ PDgm dgm1, dgm2;
+ read_diagram(filename1, dgm1);
+ read_diagram(filename2, dgm2);
+
+ std::cout << pow(wasserstein_distance(dgm1, dgm2, wasserPower), 1.0 / wasserPower) << std::endl;
+}
+
+
+void read_diagram(const std::string& filename, PDgm& dgm)
+{
+ std::ifstream in(filename.c_str());
+ double birth, death;
+ while(in)
+ {
+ in >> birth >> death;
+ //std::cout << "birth: " << birth << ", death: " << death << std::endl;
+ if (in)
+ dgm.push_back(Point(birth, death));
+ }
+}
+
+void process_program_options(int argc, char* argv[], std::string& filename1, std::string& filename2, double& wasserPower)
+{
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file1", po::value<std::string>(&filename1), "The first collection of persistence diagrams")
+ ("input-file2", po::value<std::string>(&filename2), "The second collection of persistence diagrams")
+ ("wasser-power", po::value<double>(&wasserPower), "The power of Wasserstein distance");
+
+ po::positional_options_description p;
+ p.add("input-file1", 1);
+ p.add("input-file2", 1);
+ p.add("wasser-power", 1);
+
+ po::options_description all; all.add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(p).run(), vm);
+ po::notify(vm);
+
+ if (!vm.count("input-file1") || !vm.count("input-file2"))
+ {
+ std::cout << "Usage: " << argv[0] << " input-file1 input-file2" << std::endl;
+ std::abort();
+ }
+ if (!vm.count("wasser-power"))
+ {
+ // use L2 by default
+ wasserPower = 2;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/plot-values/plot.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+
+from pylab import scatter, show, cm, colorbar, savefig, axis, \
+ figure, xlim, axes, hsv, subplots_adjust as adjust
+from itertools import izip
+from sys import argv, exit
+import os.path as osp
+
+
+def plot(val_fn, pts_fn, output_fn):
+ points = []
+ with open(pts_fn) as fp:
+ for line in fp.xreadlines():
+ points.append(map(float, line.split()))
+
+ values = []
+ with open(val_fn) as fp:
+ for line in fp.xreadlines():
+ values.append(float(line.split()[1]))
+
+ xx = [pt[0] for pt in points]
+ yy = [pt[1] for pt in points]
+ print "X:", min(xx), max(xx)
+ print "Y:", min(yy), max(yy)
+
+ m = min(values)
+ values = [(v-m) % 1. for v in values]
+ print "V:", min(values), max(values)
+
+ # hsv()
+ fig = figure()
+ scatter(xx,yy,s=10,c=values)
+ colorbar()
+
+ # ax = fig.add_axes([-.05,-.1,1.1,1.1])
+ ax = axes()
+ ax.set_axis_off()
+ ax.set_aspect('equal', 'box')
+ # adjust(0,0,1,1,0,0)
+
+ fig.savefig(output_fn)
+
+if __name__ == '__main__':
+ if len(argv) < 3:
+ print "Usage: %s VALUES POINTS" % argv[0]
+ exit()
+
+ val_fn = argv[1]
+ pts_fn = argv[2]
+ output_fn, ext = osp.splitext(val_fn)
+ output_fn += '.pdf'
+ plot(val_fn, pts_fn, output_fn)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/plot-values/scatter.py Tue Jun 27 09:37:05 2017 -0700
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+from pylab import scatter, show, cm, colorbar, axes, savefig
+from itertools import izip
+from sys import argv, exit
+import os.path as osp
+
+
+def plot(val1_fn, val2_fn, outfn = None):
+ values1 = []
+ with open(val1_fn) as fp:
+ for line in fp.xreadlines():
+ values1.append(float(line.split()[1]))
+
+ values2 = []
+ with open(val2_fn) as fp:
+ for line in fp.xreadlines():
+ values2.append(float(line.split()[1]))
+
+ values1 = [v % 1. for v in values1]
+ values2 = [v % 1. for v in values2]
+ print min(values1), max(values2), min(values1), min(values2)
+
+ scatter(values1, values2, s=10)
+ axes().set_aspect('equal')
+ if not outfn:
+ show()
+ else:
+ savefig(outfn)
+
+if __name__ == '__main__':
+ if len(argv) < 3:
+ print "Usage: %s VALUES1 VALUES2 [OUTPUT]" % argv[0]
+ exit()
+
+ val1_fn = argv[1]
+ val2_fn = argv[2]
+
+ outfn = None
+ if len(argv) > 3:
+ outfn = argv[3]
+
+ plot(val1_fn, val2_fn, outfn)