# Chain Complexes and Reduction¶

## Chain Complexes¶

A chain complex can be obtained from a simplicial or cell complex via the chain functor

```
X = bats.SimplicialComplex()
X.add([0])
X.add([1])
X.add([0,1])
C2 = bats.F2ChainComplex(X)
C3 = bats.F3ChainComplex(X)
```

You can also use `bats.Chain`

:

```
C2 = bats.Chain(X, bats.F2())
C3 = bats.Chain(X, bats.F3())
```

## Reduced Chain Complex¶

```
R2 = bats.ReducedF2ChainComplex(C2)
R2.hdim(1) # = 1
v = bats.F2Vector([1], [bats.F2(1)])
print(v[0], v[1], v[2]) # 0 1 0
R2.find_preferred_representative(v, 0)
print(v[0], v[1], v[2]) # 1 0 0
# get preferred rep for first basis element in dim 0
v = R2.get_preferred_representative(0,0)
print(v[0], v[1], v[2]) # 1 0 0
```

`bats.reduce`

¶

You can use `bats.reduce`

to create a reduced chain complex from either a `SimplicialComplex`

, or `ChainComplex`

For a chain complex:

```
C = bats.Chain(X, bats.F2())
R = bats.reduce(C)
```

You can also skip the explicit chain complex construction for `SimplicialComplex`

```
R = bats.reduce(X, bats.F2())
```

### Reduction Flags¶

You can provide a variety of flags to `bats.reduce`

which govern behavior of the reduction algorithm.

**Algorithm Flags**

`bats.standard_reduction_flag()`

- standard reduction algorithm`bats.extra_reduction_flag()`

- eliminates all entries to the right of a pivot, even when not necessary for reduction

**Optimization Flags**

`bats.clearing_flag()`

- performs clearing optimization`bats.compression_flag()`

- performs compression optimization

**Basis Flags**
By default, when you pass in flags, the basis is not computed, which is fine when you just want the betti numbers or a persistence diagram. If you want to compute the basis, you can use `bats.compute_basis_flag()`

, with either no optimizations, or the compression optimization. You can not compute the basis with the clearing optimization.

**Valid Combinations**
When using flags, you must always first provide an algorithm flag. This is followed by an optional algorithm flag, and then an optional basis flag.

```
flags = (
bats.standard_reduction_flag(), bats.compression_flag(), bats.compute_basis_flag()
)
R = bats.reduce(X, bats.F2(), *flags)
```

You can only get homology generators if you compute a basis

You must compute a basis and not use optimizations to compute induced maps.

**Performance**
Computing a basis will always be slower than not computing a basis.

Clearing or compression optimizations will almost always be faster than not using them. Which optimization is better can be problem dependent.

The choice of reduction algorithm can also affect performance. The `bats.extra_reduction_flag()`

is sometimes faster than the standard reduction - this may be because it encourages sparsity.

As a suggestion, try using

```
bats.standard_reduction_flag(), bats.compression_flag()
```

and see how this compares to just using

```
bats.standard_reduction_flag()
```

## Reducing Matrices Manually¶

At a lower level, you can use the reduction algorithm on a matrix

```
A = bats.F2Mat(3,0)
A.append_column(bats.F2Vector([(0,1), (1,1)]))
A.append_column(bats.F2Vector([(0,1), (2,1)]))
A.append_column(bats.F2Vector([(1,1), (2,1)]))
U = bats.Identity(3, bats.F2())
R =
p2c = bats.reduce_matrix(A, U)
```

This will modify the matrices A and U in-place so A is reduced, and U is the applied change of basis to columns. I.e. it maintains the invariant `A * inv(U)`

. `p2c`

will be the pivot-to-column map for the reduced matrix.