Skip to content
Merged

Dect #60

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ doc_source/notebooks/Matisse/outlines/*

benchmarks/results/*

src/ect/embed_graph.py
src/ect/embed_cw.py

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tutorial: ECT for CW complexes\n",
"\n",
"\n",
"\n",
" This tutorial walks you through how to build a CW complex with the `EmbeddedCW` class, and then use the `ECT` class to compute the Euler characteristic transform"
]
"source": "# Tutorial: ECT for CW complexes\n\nThis tutorial walks you through how to build a CW complex with the `EmbeddedCW` class, and then use the `ECT` class to compute the Euler characteristic transform.\\n\\n**Note**: This tutorial uses `EmbeddedCW`, which is now an alias for the unified `EmbeddedComplex` class. The new unified class supports arbitrary dimensional cells beyond just 2-cells (faces). See `Tutorial-EmbeddedComplex.ipynb` for comprehensive coverage of the new capabilities."
},
{
"cell_type": "code",
Expand All @@ -25,9 +19,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": [
" The CW complex is the same as the `EmbeddedGraph` class with that additional ability to add faces. Faces are added by passing in a list of vertices. Note that we are generally assuming that these vertices follow around an empty region (as in, no other vertex is in the interior) in the graph bounded by the vertices, and further that all edges are already included in the graph. However the class does not yet check for this so you need to be careful!"
]
"source": "The CW complex class extends the `EmbeddedGraph` functionality with the ability to add faces (2-cells). With the new unified `EmbeddedComplex` class, you can now add cells of any dimension!\\n\\n**Face/Cell Addition Methods:**\\n- `add_face(vertices)` - Add 2-cells (backward compatible)\\n- `add_cell(vertices, dim=k)` - Add k-cells of any dimension (new!)\\n\\nFaces are added by passing in a list of vertices. We generally assume that these vertices form a cycle bounding an empty region, and that all boundary edges are already included in the complex."
},
{
"cell_type": "code",
Expand Down Expand Up @@ -348,6 +340,28 @@
"result = ect.calculate(K)\n",
"result.plot()\n"
]
},
{
"cell_type": "code",
"source": "# Example: Adding 3-cells to create a tetrahedron\\nK_tetra = EmbeddedCW() # Still works - now creates EmbeddedComplex\\n\\n# Add tetrahedron vertices\\ntetra_vertices = {\\n 'A': [0, 0, 0],\\n 'B': [1, 0, 0],\\n 'C': [0.5, 0.866, 0],\\n 'D': [0.5, 0.289, 0.816]\\n}\\n\\nfor name, coord in tetra_vertices.items():\\n K_tetra.add_node(name, coord)\\n\\n# Add all edges\\nfrom itertools import combinations\\nfor edge in combinations(['A', 'B', 'C', 'D'], 2):\\n K_tetra.add_edge(*edge)\\n\\n# Add all triangular faces (2-cells)\\nfor face in combinations(['A', 'B', 'C', 'D'], 3):\\n K_tetra.add_face(list(face)) # Using the familiar add_face method\\n\\n# NEW: Add the 3-cell (volume)\\nK_tetra.add_cell(['A', 'B', 'C', 'D'], dim=3)\\n\\nprint(f\\\"Tetrahedron complex:\\\")\\nprint(f\\\" 0-cells (vertices): {len(K_tetra.nodes())}\\\")\\nprint(f\\\" 1-cells (edges): {len(K_tetra.edges())}\\\")\\nprint(f\\\" 2-cells (faces): {len(K_tetra.faces)}\\\")\\nprint(f\\\" 3-cells (volumes): {len(K_tetra.cells[3])}\\\")\\n\\n# Plot the tetrahedron\\nfig = plt.figure(figsize=(8, 6))\\nax = fig.add_subplot(111, projection='3d')\\nK_tetra.plot(ax=ax, face_alpha=0.3, node_size=100)\\nax.set_title('Tetrahedron with 3-cell')\\nplt.show()\\n\\n# Compute ECT (now includes the 3-cell in the calculation!)\\nect_tetra = ECT(num_dirs=20, num_thresh=30)\\nresult_tetra = ect_tetra.calculate(K_tetra)\\nresult_tetra.plot()\\nplt.title('ECT of Tetrahedron (includes 3-cell contribution)')\\nplt.show()\"",
"metadata": {},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "## New Capabilities: Beyond 2-Cells\\n\\nWith the unified `EmbeddedComplex` class, you can now add cells of arbitrary dimension. Here's a quick example showing 3-cells:\"",
"metadata": {}
},
{
"cell_type": "markdown",
"source": "## Enhanced ECT Computation\\n\\nThe ECT now properly computes the Euler characteristic using the alternating sum over **all** cell dimensions:\\n\\n**χ(threshold) = Σ(-1)^k × |{k-cells with projection ≤ threshold}|**\\n\\nThis means:\\n- 0-cells (vertices) contribute **+1** each\\n- 1-cells (edges) contribute **-1** each \\n- 2-cells (faces) contribute **+1** each\\n- 3-cells (volumes) contribute **-1** each\\n- And so on...\\n\\nCompare this to the traditional graph-only ECT which only considered vertices and edges!\"",
"metadata": {}
},
{
"cell_type": "markdown",
"source": "## Summary and Next Steps\\n\\nThis tutorial showed CW complex functionality using `EmbeddedCW`. Key takeaways:\\n\\n- **Backward Compatibility**: All existing `EmbeddedCW` code continues to work\\n- **Enhanced Capabilities**: The unified `EmbeddedComplex` now supports arbitrary dimensional cells\\n- **Improved ECT**: Properly includes all cell dimensions in Euler characteristic computation\\n\\n### For More Advanced Features:\\n- See `Tutorial-EmbeddedComplex.ipynb` for comprehensive examples of high-dimensional cells\\n- The unified interface supports complexes with cells of any dimension\\n- Enhanced visualization and analysis capabilities\\n\\n### Migration Guide:\\n```python\\n# Old way (still works):\\nfrom ect import EmbeddedCW\\nK = EmbeddedCW()\\n\\n# New way (same result, more features):\\nfrom ect import EmbeddedComplex \\nK = EmbeddedComplex()\\n\\n# Both create the same object with identical functionality!\\n```\"",
"metadata": {}
}
],
"metadata": {
Expand All @@ -371,4 +385,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tutorial: ECT for Embedded Graphs\n",
"\n",
"\n",
"\n",
"This tutorial demonstrates how to use the `ect` package..."
]
"source": "# Tutorial: ECT for Embedded Graphs\n\nThis tutorial demonstrates how to use the `ect` package to compute the Euler Characteristic Transform (ECT) for embedded graphs.\n\n**Note**: This tutorial uses `EmbeddedGraph`, which is now an alias for the unified `EmbeddedComplex` class. All functionality shown here works identically, but `EmbeddedComplex` offers additional features like arbitrary dimensional cells. See the `Tutorial-EmbeddedComplex.ipynb` for the full capabilities."
},
{
"cell_type": "code",
Expand All @@ -25,14 +19,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic Usage\n",
"\n",
"\n",
"\n",
"First, let's create a simple graph\"\"\"\n",
"\n"
]
"source": "## Basic Usage\n\nFirst, let's create a simple graph. The `EmbeddedGraph` class (now unified with `EmbeddedComplex`) allows you to create graphs with vertices embedded in Euclidean space."
},
{
"cell_type": "code",
Expand Down Expand Up @@ -87,11 +74,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": [
" The embedded graph class inherits from the networkx graph class with the additional attributes `coord_matrix` and `coord_dict`.\n",
"\n",
" The coordinates of all vertices can be accessed using the `coord_matrix` attribute."
]
"source": "The embedded graph class inherits from the networkx graph class with additional attributes for spatial embedding. The unified `EmbeddedComplex` class provides:\\n\\n- `coord_matrix`: N×D matrix of vertex coordinates\\n- `node_list`: Ordered list of vertex identifiers \\n- `cells`: Dictionary storing k-cells by dimension (new feature)\\n\\nThe coordinates of all vertices can be accessed using the `coord_matrix` attribute."
},
{
"cell_type": "code",
Expand Down Expand Up @@ -770,6 +753,11 @@
"result_3d = ect_3d.calculate(graph_3d)\n",
"result_3d.plot()\n"
]
},
{
"cell_type": "markdown",
"source": "## What's New: Unified EmbeddedComplex\\n\\nThis tutorial showed the graph functionality using `EmbeddedGraph`. The new unified `EmbeddedComplex` class provides all this functionality plus:\\n\\n- **Arbitrary dimensional cells**: Add 2-cells (faces), 3-cells (volumes), and higher\\n- **Enhanced ECT computation**: Properly includes all cell dimensions in Euler characteristic\\n- **Backward compatibility**: `EmbeddedGraph` is now an alias to `EmbeddedComplex`\\n\\n### Quick example with the same graph:\\n\\n```python\\n# This graph can now also have faces!\\nG.add_face(['A', 'B', 'C']) # Add a 2-cell if vertices form a triangle\\n\\n# Or even higher dimensional cells\\nG.add_cell(['A', 'B', 'C', 'D'], dim=3) # 3-cell if you have 4 vertices\\n```\\n\\nSee `Tutorial-EmbeddedComplex.ipynb` for comprehensive examples of these new capabilities!\"",
"metadata": {}
}
],
"metadata": {
Expand All @@ -788,4 +776,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
3 changes: 3 additions & 0 deletions doc_source/directions.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Directions

The `Directions` class provides helper functions for selecting directions to compute the ECT along.


```{eval-rst}
.. automodule:: ect.directions
:members:
Expand Down
7 changes: 6 additions & 1 deletion doc_source/ect_on_graphs.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# ECT on Graphs

```{eval-rst}
.. automodule:: ect.ect_graph
.. automodule:: ect.ect
:members:
```

```{eval-rst}
.. automodule:: ect.sect
:members:
```

```{eval-rst}
.. automodule:: ect.dect
:members:
```
46 changes: 46 additions & 0 deletions doc_source/embed_complex.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Embedded Complex

The `EmbeddedComplex` class is a unified representation for embedded cell complexes supporting arbitrary dimensional cells.

## Overview

`EmbeddedComplex` combines and extends the functionality of the previous `EmbeddedGraph` and `EmbeddedCW` classes into a single interface. It supports:

- **0-cells (vertices)**: Points embedded in Euclidean space
- **1-cells (edges)**: Line segments between vertices
- **k-cells for k ≥ 2**: Higher dimensional cells (faces, volumes, etc.)


## Basic Usage

```python
from ect import EmbeddedComplex

# Create a complex
K = EmbeddedComplex()

# Add vertices
K.add_node("A", [0, 0])
K.add_node("B", [1, 0])
K.add_node("C", [0.5, 1])

# Add edges
K.add_edge("A", "B")
K.add_edge("B", "C")
K.add_edge("C", "A")

# Add a 2-cell (face)
K.add_face(["A", "B", "C"])

# Or use the general method for any dimension
K.add_cell(["A", "B", "C"], dim=2)
```

## API Reference

```{eval-rst}
.. automodule:: ect.embed_complex
:members:
:show-inheritance:
:undoc-members:
```
6 changes: 0 additions & 6 deletions doc_source/embed_cw.md

This file was deleted.

6 changes: 0 additions & 6 deletions doc_source/embed_graph.md

This file was deleted.

8 changes: 4 additions & 4 deletions doc_source/index.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ect: Euler Characteristic Transform in Python
=============================================

Python computation tools for computing the Euler Characteristic Transform of embedded graphs.
Python computation tools for computing the Euler Characteristic Transform of embedded cell complexes with arbitrary dimensional cells.

Table of Contents
-----------------
Expand All @@ -13,15 +13,15 @@ Table of Contents

Getting Started <installation.rst>
Modules <modules.rst>
Tutorials <tutorials.md>
Tutorials <tutorials.rst>
Contributing <contributing.rst>
License <license.md>
Citing <citing.rst>

Description
-----------

Right now, the content includes stuff for doing ECT on graphs embedded in 2D. Eventually the goal is to get voxel versions, higher dimensional simplicial complexes, etc in here.
This package provides tools for computing the Euler Characteristic Transform (ECT) of embedded cell complexes efficienctly and provides many useful utilities for visualizing and comparing different ECTs.

For more information on the ECT, see:

Expand All @@ -34,7 +34,7 @@ Documentation and tutorials
^^^^^^^^^^^^^^^^^^^^^^^^^^^

* The documentation is available at: `munchlab.github.io/ect <https://munchlab.github.io/ect/>`_
* A tutorial jupyter notebook can be found `here <https://munchlab.github.io/ect/notebooks/Tutorial-ECT_for_embedded_graphs.html>`_
* A comprehensive tutorial for the unified `EmbeddedComplex` class can be found `here <https://munchlab.github.io/ect/notebooks/Tutorial-EmbeddedComplex.html>`_
* The source code can be found at: `github.com/MunchLab/ect <https://github.com/MunchLab/ect>`_

Dependencies
Expand Down
4 changes: 2 additions & 2 deletions doc_source/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Table of Contents
:maxdepth: 2


Embedded graphs <embed_graph.md>
Embedded CW complex <embed_cw.md>
Embedded Complex <embed_complex.md>
Validation System <validation.md>
ECT on graphs <ect_on_graphs.md>
Directions <directions.md>
97 changes: 9 additions & 88 deletions doc_source/notebooks/Matisse/example_matisse.ipynb

Large diffs are not rendered by default.

381 changes: 381 additions & 0 deletions doc_source/notebooks/Tutorial-EmbeddedComplex.ipynb

Large diffs are not rendered by default.

96 changes: 10 additions & 86 deletions doc_source/notebooks/Tutorial-ExactECT.ipynb

Large diffs are not rendered by default.

374 changes: 0 additions & 374 deletions doc_source/notebooks/tutorial_cw.ipynb

This file was deleted.

791 changes: 0 additions & 791 deletions doc_source/notebooks/tutorial_graph.ipynb

This file was deleted.

5 changes: 2 additions & 3 deletions doc_source/tutorials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ Tutorials
:maxdepth: 2
:caption: Contents:

notebooks/tutorial_graph
notebooks/tutorial_cw
notebooks/Tutorial-EmbeddedComplex
notebooks/Tutorial-ExactECT
notebooks/Matisse/example_matisse
.. notebooks/Tutorial-ExactECT

114 changes: 114 additions & 0 deletions doc_source/validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Validation System

We often require our cell complexes to satisfy geometric constraints. The validation system provides modular, extensible validation for embedded cell complexes to ensure they represent proper embeddings in Euclidean space.

## Overview

The validation system distinguishes between two types of rules:

- **Structural Rules** (always checked): Basic requirements like vertex counts and dimension validity
- **Geometric Rules** (optional): Embedding properties like non-intersecting edges and faces

## Architecture

The validation system consists of several components:

1. **Base Classes**: Abstract interfaces for validation rules and results
2. **Validation Rules**: Concrete implementations for specific validation checks
3. **Validator**: Main orchestrator that manages and applies rules

## Validation Rules

### Structural Rules (Always Enforced)

- **DimensionValidityRule**: Ensures cell dimensions are non-negative
- **VertexCountRule**: Validates correct vertex counts for cell dimensions
- 0-cells must have exactly 1 vertex
- 1-cells must have exactly 2 vertices
- k-cells (k ≥ 2) must have at least 3 vertices

### Geometric Rules (Optional)

- **EdgeInteriorRule**: Ensures no vertices lie on edge interiors
- **FaceInteriorRule**: Ensures no vertices lie inside face interiors
- **SelfIntersectionRule**: Validates that face edges don't self-intersect
- **BoundaryEdgeRule**: Ensures required boundary edges exist for faces

## Usage

```python
from ect import EmbeddedComplex

# Enable validation during construction
K = EmbeddedComplex(validate_embedding=True)

# Or enable/disable later
K.enable_embedding_validation(tol=1e-10)
K.disable_embedding_validation()

# Override per operation
K.add_cell(vertices, dim=2, check=True)
```

## Custom Validation Rules

You can create custom validation rules by inheriting from `ValidationRule`:

```python
from ect.validation import ValidationRule, ValidationResult

class MyCustomRule(ValidationRule):
@property
def name(self) -> str:
return "My Custom Rule"

@property
def is_structural(self) -> bool:
return False # Geometric rule

def applies_to_dimension(self, dim: int) -> bool:
return dim == 2 # Only for 2-cells

def validate(self, cell_coords, all_coords, cell_indices, all_indices, dim):
# Your validation logic here
if some_condition:
return ValidationResult.valid()
else:
return ValidationResult.invalid("Validation failed")

# Add to validator
K.get_validator().add_rule(MyCustomRule())
```

## API Reference

### Main Module

```{eval-rst}
.. automodule:: ect.validation
:members:
```

### Base Classes

```{eval-rst}
.. automodule:: ect.validation.base
:members:
:show-inheritance:
```

### Validation Rules

```{eval-rst}
.. automodule:: ect.validation.rules
:members:
:show-inheritance:
```

### Validator

```{eval-rst}
.. automodule:: ect.validation.validator
:members:
:show-inheritance:
```
File renamed without changes.
Binary file added docs/.doctrees/directions.doctree
Binary file not shown.
Binary file added docs/.doctrees/ect_on_graphs.doctree
Binary file not shown.
Binary file added docs/.doctrees/embed_complex.doctree
Binary file not shown.
Binary file added docs/.doctrees/environment.pickle
Binary file not shown.
Binary file added docs/.doctrees/index.doctree
Binary file not shown.
File renamed without changes.
Binary file not shown.
Loading
Loading