~mht/cra

f53bbcd49cbee65e0810cdee1eddbd785c5ced4c — Martin Hafskjold Thoresen 9 months ago 933b273
Move code to top level
28 files changed, 290 insertions(+), 204 deletions(-)

A .gitignore
A Cargo.lock
R cra/{Cargo.toml => o.toml}
M Makefile
R cra/{README.md => ME.md}
D algos.tex
D bib.bib
D cra/.gitignore
D cra/Makefile
R cra/data/{circle.points => le.points}
R cra/data/{draw.points => .points}
R cra/data/{flower.points => er.points}
R cra/data/{hp.points => oints}
R cra/data/{rand-l.points => -l.points}
R cra/data/{rand.points => .points}
R cra/data/{sample1.points => le1.points}
R cra/data/{slice.points => e.points}
R cra/data/{tet.boundary => boundary}
R cra/data/{twoc.points => .points}
R cra/{generate-points.py => rate-points.py}
R cra/{listings.tex => ings.tex}
D main.tex
R cra/src/{log.rs => rs}
R cra/src/{main.rs => .rs}
R cra/src/{output.rs => ut.rs}
R cra/src/{persistence.rs => istence.rs}
D text.tex
R cra/{triangulate2.cpp => ngulate2.cpp}
A .gitignore => .gitignore +2 -0
@@ 0,0 1,2 @@
/target
**/*.rs.bk

A Cargo.lock => Cargo.lock +218 -0
@@ 0,0 1,218 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
 "winapi",
]

[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi",
 "libc",
 "winapi",
]

[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "clap"
version = "2.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
dependencies = [
 "ansi_term",
 "atty",
 "bitflags",
 "strsim",
 "textwrap",
 "unicode-width",
 "vec_map",
]

[[package]]
name = "cra"
version = "0.1.0"
dependencies = [
 "clap",
 "lazy_static",
 "serde",
 "serde_json",
 "time",
]

[[package]]
name = "hermit-abi"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
dependencies = [
 "libc",
]

[[package]]
name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "libc"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"

[[package]]
name = "proc-macro2"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
dependencies = [
 "unicode-xid",
]

[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"

[[package]]
name = "ryu"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"

[[package]]
name = "serde"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "serde_json"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
dependencies = [
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"

[[package]]
name = "syn"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-xid",
]

[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
 "unicode-width",
]

[[package]]
name = "time"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
dependencies = [
 "libc",
 "redox_syscall",
 "winapi",
]

[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"

[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"

[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"

[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

R cra/Cargo.toml => Cargo.toml +3 -1
@@ 2,7 2,9 @@
name = "cra"
version = "0.1.0"
authors = ["Martin Hafskjold Thoresen <git@mht.wtf>"]
description = "blablabla"
description = """
CRA implements the matrix reduction algorithm to compute persistence in a complex.
"""

[dependencies]
time = "0.1.42"

M Makefile => Makefile +42 -3
@@ 1,4 1,43 @@
texfiles = main.tex text.tex
POINT_FILES=$(shell find data -name "*.points")

writeup.pdf: $(texfiles)
	latex-mk --pdflatex main
help:
	echo $(POINT_FILES)

# These files contains the a triangulation. Point and face
# info is in the file, edge info is implicit in the face info.
data/%.tri: data/%.points triangulate2
	./triangulate2 < $^ > $@

# Cargo eh
target/release/cra: src/main.rs src/output.rs src/persistence.rs
	cargo build --release

# Output from the reduction algorithm
output/.%: data/%.tri target/release/cra
	-mkdir -p output/$*
	env RUST_BACKTRACE=1 target/release/cra -t="-" < $< \
		--graphviz=output/$*/graph.dot \
		--diagram=output/$*/persistence.pdf \
		--svg=output/$*/rendering.svg \
		--tex-table=output/$*/perf-stats.tex \
		--stats=output/$*/stats \
		--points="-"
	convert -size 800x800 output/$*/rendering.svg output/$*/rendering.png
	touch $@

# Triangulation exec
triangulate2: triangulate2.cpp
	clang++ -O2 -Wall triangulate2.cpp -DCGAL_HEADER_ONLY -lgmp -o triangulate2  

# Complete .pdf with all graphs in it for all data sets.
listings.pdf: listings.tex all-pdfs
	latex-mk --pdflatex listings.tex

# Make all folders in `output` for each `.point` file.
all-pdfs: | $(foreach F,$(POINT_FILES),$(patsubst data/%.points,output/.%,$(F)))

.PHONY:
clean:
	rm -fr *.tri *.svg triangulate2

.PRECIOUS: data/%.out data/%.tri output/%/rendering.svg

R cra/README.md => README.md +0 -0

D algos.tex => algos.tex +0 -10
@@ 1,10 0,0 @@
\section{Algorithms}
\def\code#1{\texttt{#1}}

The algorithms works by going from left to right over the columns of the
matrix, and maintaining the highest index of a \code{1} in each column (the
\code{low}).  For each column we look at the column to the left of it, and if
two columns have the same \code{low}, we subtract the leftmost from the
rightmost column. The exhausitve version differs here in that it looks through
the indices \code{i} of all \code{1}s of the current column, and adds any
column \code{j} that has \code{low(j) = i}.

D bib.bib => bib.bib +0 -0

D cra/.gitignore => cra/.gitignore +0 -14
@@ 1,14 0,0 @@
data/*.svg
data/*.pdf
data/*.pdf
data/*.tri
data/*.out
data/*.png

/target

/*.pdf
/pdfs

/lol*
/triangulate2

D cra/Makefile => cra/Makefile +0 -43
@@ 1,43 0,0 @@
POINT_FILES=$(shell find data -name "*.points")

help:
	echo $(POINT_FILES)

# These files contains the a triangulation. Point and face
# info is in the file, edge info is implicit in the face info.
data/%.tri: data/%.points triangulate2
	./triangulate2 < $^ > $@

# Cargo eh
target/release/cra: src/main.rs src/output.rs src/persistence.rs
	cargo build --release

# Output from the reduction algorithm
output/.%: data/%.tri target/release/cra
	-mkdir -p output/$*
	env RUST_BACKTRACE=1 target/release/cra -t="-" < $< \
		--graphviz=output/$*/graph.dot \
		--diagram=output/$*/persistence.pdf \
		--svg=output/$*/rendering.svg \
		--tex-table=output/$*/perf-stats.tex \
		--stats=output/$*/stats \
		--points="-"
	convert -size 800x800 output/$*/rendering.svg output/$*/rendering.png
	touch $@

# Triangulation exec
triangulate2: triangulate2.cpp
	clang++ -O2 -Wall triangulate2.cpp -DCGAL_HEADER_ONLY -lgmp -o triangulate2  

# Complete .pdf with all graphs in it for all data sets.
listings.pdf: listings.tex all-pdfs
	latex-mk --pdflatex listings.tex

# Make all folders in `output` for each `.point` file.
all-pdfs: | $(foreach F,$(POINT_FILES),$(patsubst data/%.points,output/.%,$(F)))

.PHONY:
clean:
	rm -fr *.tri *.svg triangulate2

.PRECIOUS: data/%.out data/%.tri output/%/rendering.svg

R cra/data/circle.points => data/circle.points +0 -0

R cra/data/draw.points => data/draw.points +0 -0

R cra/data/flower.points => data/flower.points +0 -0

R cra/data/hp.points => data/hp.points +0 -0

R cra/data/rand-l.points => data/rand-l.points +0 -0

R cra/data/rand.points => data/rand.points +0 -0

R cra/data/sample1.points => data/sample1.points +0 -0

R cra/data/slice.points => data/slice.points +0 -0

R cra/data/tet.boundary => data/tet.boundary +0 -0

R cra/data/twoc.points => data/twoc.points +0 -0

R cra/generate-points.py => generate-points.py +0 -0

R cra/listings.tex => listings.tex +0 -0

D main.tex => main.tex +0 -60
@@ 1,60 0,0 @@
\documentclass[]{article}

\usepackage[utf8]{inputenc}             % UTF-8 input please
\usepackage{libertine}                  % The best font
\usepackage{libertinust1math}           % ... and its Math variant
\usepackage{inconsolata}                % A sane monospace default
\usepackage{amsmath}                    % Misc. math stuff
\usepackage{amsthm}                     % Theorems etc.
% \usepackage{hyperref}                   % Clickable links
% \usepackage{cleveref}                   % \cref
\usepackage[inline]{enumitem}           % inline enumeration
% \usepackage{subcaption}                 % Subfigures
\usepackage{booktabs}                   % Rules in tables
% \usepackage{tikz}			% Graph figures
% \usetikzlibrary{fit,positioning,shapes} % ^.. libraries
% Packages we only need when writing:
% \usepackage{color}                      % Colors
% idk, tikz complained
% \usepackage{todo}                       % TODO: notes
\usepackage[left=1cm, 
            right=1cm,
            top=2cm, 
            bottom=2cm,
            % not A4
            a5paper,
            ]{geometry}       % Smaller page margins
\usepackage{parskip}			% Maybe don't have indents for paragraphs

% \definecolor{lightgrey}{HTML}{f0f0f0}

\newtheorem{definition}{Definition}
\newtheorem{lemma}{Lemma}
% \crefname{definition}{Definition}{Definitions}

\newcommand{\dan}[1]{{\texttt{\color{blue} Dan: [{#1}]}}}
\newcommand{\martin}[1]{{\texttt{\color{red} Martin: [{#1}]}}}


\def\Exp{\mathbb{E}}
\newcommand{\algo}[1]{\textsc{#1}}              % Style for algorithm names like `Union`
\newcommand{\algocall}[2]{\textsc{#1(\(#2\))}}  % .. and if we want to call it: `Union(a, b)`


\title{Notes on Homology}
\author{Martin Hafskjold Thoresen}
\date{\today}

\begin{document}

\maketitle

\input{text.tex}


\input{algos.tex}

% \bibliographystyle{acm}
% \bibliography{bib}

\end{document}

R cra/src/log.rs => src/log.rs +0 -0

R cra/src/main.rs => src/main.rs +25 -7
@@ 292,23 292,30 @@ fn main() {
        (about: crate_description!())
        (version: crate_version!())
        (author: crate_authors!())
        (@arg triangulation: -t --triangulation[FILE] "Input a 2D triangulation in text form.")
        // (@arg triangulation: -t --triangulation[FILE] "Input a 2D triangulation in text form.")
        (@arg exhaustive: -e --exhaustive "Run the exhaustive variant of the algorithm.")
        (@arg svg: --svg[FILE] requires[points] "Draw the compex to an .svg.")
        (@arg alpha: -a --alpha requires[points] "Ignore the ordering of the simplices given and calculate and use the alpha ordering.")
        // (@arg svg: --svg[FILE] requires[points] "Draw the compex to an .svg.")
        // (@arg alpha: -a --alpha requires[points] "Ignore the ordering of the simplices given and calculate and use the alpha ordering.")
        (@arg stats: -s --stats[FILE] "Print out statistics.")
        (@arg graphviz: --graphviz[FILE] "Use `graphviz` to graph out which simplices are added together.")
        (@arg diagram: --diagram[FILE] "Use `gnuplot` to print out the persistence diagram.")
        (@arg points: --points[FILE] "Input the location data for the 1-simplices.")
        // (@arg graphviz: --graphviz[FILE] "Use `graphviz` to graph out which simplices are added together.")
        // (@arg diagram: --diagram[FILE] "Use `gnuplot` to print out the persistence diagram.")
        // (@arg points: --points[FILE] "Input the location data for the 1-simplices.")
        (@arg debug: -d "Print debug information to `stdout`")
    ).arg(Arg::from_usage(
            "<boundary> -b, --boundary <FILE> 'The boundary of all simplices in the complex.'",
        ).long_help("\
This file should contain the boundaries of your simplices.
The boundary of the empty complex and the vertices are implied, and so
the first boundaries in the list should be that of the 1-simplices.\n "),
the first boundaries in the list should be that of the 1-simplices.

The first line of the boundary file should be the number of points in the complex.
Subsequent lines each contain the boundary of a simplex as a list of simplex IDs. The ID of a simplex
is it's sequence number in the file. See `data/tet.boundary` for an example of a tetrahedron.
"),
    ).get_matches();

    let mut was_useful = false;

    let debug = matches.is_present("debug");

    let mut simplices = {


@@ 351,12 358,14 @@ the first boundaries in the list should be that of the 1-simplices.\n "),
        reduction
            .debug_output(&mut out, &persistence.simplices)
            .unwrap();
        was_useful = true;
    }

    if let Some(path) = matches.value_of("stats") {
        let mut stats_file = open_file_or_stdout(path).unwrap();
        let json = serde_json::to_string(&reduction.log.aggregate()).expect("failed to convert stats to .json");
        write!(stats_file, "{}", json).expect("failed to write stats to file");
        was_useful = true;
    }

    // if let Some(path) = matches.value_of("textable") {


@@ 368,6 377,7 @@ the first boundaries in the list should be that of the 1-simplices.\n "),
    if let Some(path) = matches.value_of("graphviz") {
        let mut f = File::create(Path::new(path)).unwrap();
        output::graphviz(&persistence, &reduction, &reduction, &mut f).unwrap();
        was_useful = true;
    }

    if let Some(path) = matches.value_of("diagram") {


@@ 377,9 387,17 @@ the first boundaries in the list should be that of the 1-simplices.\n "),
            .map(|&(a, b)| (persistence.alphas[a], persistence.alphas[b]))
            .collect::<Vec<(f64, f64)>>();
        output::persistence(&birth_death_pairs, &Path::new(path));
        was_useful = true;
    }

    if let Some(path) = matches.value_of("svg") {
        output::svg(&persistence, &reduction, &reduction, &Path::new(path));
        was_useful = true;
    }

    if !was_useful {
        eprintln!("It doesn't look like you actually got anything useful here!");
        eprintln!("See `cra --help` if you are confused.");
        eprintln!("{}", matches.usage());
    }
}

R cra/src/output.rs => src/output.rs +0 -0

R cra/src/persistence.rs => src/persistence.rs +0 -0

D text.tex => text.tex +0 -66
@@ 1,66 0,0 @@
\section{Words and things}

\def\cp{\textsf{c}_p}
\def\cpm1{\textsf{c}_{p-1}}
\def\boundary#1{\partial #1}
\def\xor{\texttt{XOR}}
\def\homgroup{\tilde{H}_{p}(K)}

\begin{definition}[p-chain]
$\cp \subseteq K^p$: some of the p-cells of dimension $p$.
The set of all p-chains is a complex $K$ is $C_p(K)$.
\end{definition}

\begin{definition}[boundary]
The boundary of a p-chain $\cp$ is a $(p-1)$-chain $\cpm1$ such that  
all cells in $\cpm1$ share an odd number of cells in $\cp$.
\end{definition}

\begin{definition}[p-boundary]
A p-boundary is the boundary of a p-chain.
The set of all p-boundaries is a complex $K$ is $B_p(K)$.
\end{definition}

\begin{definition}[p-cycle]
A p-cycle is a p-chain which boundary is $\emptyset$.
The set of all p-cycles is a complex $K$ is $Z_p(K)$.
\end{definition}

Remember that we have a $(-1)$-cell, which is the empty cell. This is a face
of all other cells, which means that the boundary of a single vertex 
is the empty cell, so a single vertex is not a 0-cycle.
Two vertices are a 0-cycle however: 
the empty cell is shared between both vertices, so the boundary is $\emptyset$,
making it a 0-cycle.


Apparently, $\boundary{(a + b)} = \boundary{a} + \boundary{b}$.
Then we can show why the definition of a boundary makes sense:
let $P$ be a $p$-chain, and look at the $p$-cells in $P$ (here denoted $p$):
The boundary of $P$ is 
\[
\boundary{P} = \boundary{\sum_{p \in P} p} = \sum_{p \in P} \boundary{p} = \sum_{p \in P}\sum_{f \in p} f 
\]
Since we're operating with \xor{} addition, the $(p-1)$ faces that survive this is exactly
the ones that are shared by an odd number of $p$-cells in $P$.

\begin{lemma}
The boundary of a boundary is $\emptyset$.
\begin{proof}

\end{proof}
\end{lemma}

The boundaries are the trivial cycles of a $p$-chain. From the conversation
with Ondra, this is analogous to a vector space in which we chose one direction
to be trivial, and have from then all all vectors that differ by this trivial
direction be the same.  In this context, the trivial cycles are the ``zeroes'',
and so adding a boundary to a cycle doesn't really ``change'' it: we put both
chains into the same class, and denote the set of all classes by $\homgroup{} =
Z_p(K)/B_p(K)$, and the \emph{Betti number} is the rank of $\homgroup{}$, that
is the number of non-trivial cycles.

A geometrical intuition for why we have chosen the boundaries to be the trivial
cycles is that these boundaries are filled, since there is a $p$-chain that
have this $(p-1)$-chain as a boundary, and we're interested in the cycles that
are not filled.

R cra/triangulate2.cpp => triangulate2.cpp +0 -0