--- 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;