Skip to content

Commit

Permalink
bump rc version 0.0.0rc0
Browse files Browse the repository at this point in the history
- repackage
- migrate old README.md to Read the Docs
  • Loading branch information
aletgn committed May 2, 2024
1 parent 18c3fbd commit 5080ba4
Show file tree
Hide file tree
Showing 19 changed files with 298 additions and 272 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode
__pycache__
*.bin
*.egg-info
Expand Down
20 changes: 2 additions & 18 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -1,32 +1,16 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.12"
# You can also specify other tool versions:
# nodejs: "19"
# rust: "1.64"
# golang: "1.19"

# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/conf.py

# Optionally build your docs in additional formats such as PDF and ePub
# formats:
# - pdf
# - epub
formats:
- pdf

# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- requirements: docs/requirements.txt
255 changes: 12 additions & 243 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,256 +1,25 @@
# PyToM-3D: Python Topography Manipulator in 3D

PyToM-3D is a Python package to transform and fit 3-dimensional topographies. It provides methods to perform traditional geometric transformations and visualise the results as well.
PyToM-3D is a Python package to transform and fit 3-dimensional topographies. It provides methods to perform traditional geometric transformations and visualise the results as well. Please note that the package is still in its development stage.

[Geometric Transformations](#geometric-transformations)
## Features

- [Translation](#translation)
- Geometric Transformations;

- [Rotation](#rotation)
- Singular Value Decomposition;

- [Flip](#flip)
- Data Regression;

- [Cut](#cut)
- Data Inspection.

[Singular Value Decomposition (SVD)](#svd)
## Installation

- [Introduction](#intro-svd)

- [Using SVD](#using-svd)

[Data Regression](#data-regression)

- [Gaussian Process Regression](#gpr)

- [Spline Fitting](#spline)

[Data Inspection](#data-inspection)

- [2D Views](#2d)

- [3D Views](#3d)

# Initialising a Topography

Initially, we need to istantiate a `Topography`:

```python
t = Topography()
```

We have a multitude of options:

- reading from a `.csv` file;

- generating a grid and add synthetic data.

# Geometric Transformations <a name="geometric-transformations"></a>

In order to understand each geometric transformation remember that the points of the topography are stored in the attribute ```P```, which is a $N\times 3$ matrix:

$$\mathbf{P} = \begin{bmatrix} x_1 & y_1 & z_1 \\\ x_2 & y_2 & z_2 \\\ \vdots & \vdots & \vdots \\\ x_N & y_N & z_N\end{bmatrix},$$

where $\mathbf{p}_i = \left[x_i\quad y_i \quad z_i\right]$ is the *i*th point of the topography. In practice the altitude $z_i$ is determined upon a latent function of the coordinates, hence $z_i = f(x_i, y_i)$.

## Translation <a name="translation"></a>

Let us define a vector $\mathbf{v} = \left[x_v\quad y_v\quad z_v \right]$. By calling:

```python
t.translate([xv,yv,zv])
```

we translate $\mathbf{P}$ by $\mathbf{v}$ as:

$$\mathbf{p}_i \leftarrow \mathbf{p}_i + \mathbf{v}\quad\forall\ i = 1,2,\dots,N.$$

## Rotation <a name="rotation"></a>
This method performs the rotation about the centre given three rotation angles $\theta_x$, $\theta_y$, $\theta_z$. The method builds the corresponding rotation matrices:

$$\mathbf{R}_x = \begin{bmatrix} 1 & 0 & 0 \\\ 0 & \cos(\theta_x) & \sin(\theta_x) \\\ 0 & -\sin(\theta_x) & \cos(\theta_x) \\\ \end{bmatrix},$$

$$\mathbf{R}_y = \begin{bmatrix} \cos(\theta_y) & 0 & \sin(\theta_y) \\\ 0 & 1 & 0 \\\ -\sin(\theta_y) & 0 & \cos(\theta_y) \end{bmatrix},$$

$$\mathbf{R}_z = \begin{bmatrix} \cos(\theta_z) & \sin(\theta_z) & 0 \\\ -\sin(\theta_z) & \cos(\theta_z) & 0 \\\ 0 & 0 & 1 \end{bmatrix},$$

$$\mathbf{R} = \mathbf{R}_x\mathbf{R}_y\mathbf{R}_z.$$

Then each point is rotated as:

$$\mathbf{p}_i \leftarrow \mathbf{R}\mathbf{p}_i.$$

This is accomplished via:

```python
t.rotate(t_deg=[t_x, t_y, t_z])
```

The method supports passing a rotation matrix too:

```python
t.rotate(rot_mat=R)
```

In case on wishes to rotate about a given centre $c=\left[c_x\quad c_y\quad c_z\right]^\top$, they would call the wrapper method:

```pyhton
t.rotate_about_centre(c=[c_x, c_y, c_z]t_deg=[t_x, t_y, t_z])
```

or by providing a rotation matrix `rot_mat`.


## Flip <a name="flip"></a>

This method allows mirroring data with respect to a given vector $\mathbf{v}=\left[v_x\quad v_y\quad v_z \right]$ which represent the outward normal of the intended flipping plane. Assuming one wishes to flip the data about the $yz$ plane, they would define $\mathbf{v}=\left[-1\quad 1\quad 1 \right]$, and call:

```python
t.flip([-1,1,1])
```

This operation performs $(\mathbf{p}_i = \left[x_i\quad y_i \quad z_i\right])$:

$$\left[x_i\quad y_i \quad z_i\right] \leftarrow \left[x_i\cdot v_x\quad y_i\cdot vy\quad z_i\cdot vz\right] \quad\forall\ i = 1,2,\dots,N.$$

## Cut <a name="cut"></a>

Suppose you would like to remove some outliers from topography. Although the same procedure applies to the other axes identically, we focus on the z-axis. We also assume two threshold $l$ and $u$, whereby we filter each $i$th datum using the criterion:

$$z_i \leftarrow z_i:\quad z_i > l\quad \text{and}\quad z_i < u.$$

To do so, we call:

```python
t.cut(ax="z", lo=l, up=u, out=False)
```

If `out=True` the method keep the points complying with:

$$z_i \leftarrow z_i:\quad z_i < l\quad \text{and}\quad z_i > u.$$

# Singular Value Decomposition (SVD) <a name="svd"></a>

## Introduction <a name="intro-svd"></a>
Let $\mathbf{P}$ be a generic matrix belonging to $\mathbb{R}^{m\times n}$. SVD allows $\mathbf{P}$ to be decomposed into the product of three matrices:

$$\mathbf{P} = \mathbf{U}\mathbf{S}\mathbf{V^\top},$$

where $\mathbf{U}\in \mathbb{R}^{m\times m}$ , and $\mathbf{V}^\top\in \mathbb{R}^{n\times n}$ are called respectively *left-* and *right-principal* matrix (both orthonormal), whereas $\mathbf{S}\in \mathbb{R}^{m\times n}$ is the so-called Singular Value Matrix.

We do SVD via:

```python
t.svd()
```

## Using SVD <a name="using-svd"></a>

Suppose that the points of the topography are distributed according to three preferred directions, which are identifiable by visual inspection. Assume that these directions define a reference frame $`\{x_s,y_s,z_s\}`$. Conversely, assume that the points forming the point topography have been acquired with respect to another (arbitrary) reference frame $`\{x,y,z\}`$, which (unfortunately) is not aligned with $`\{x_s,y_s,z_s\}`$. However, expressing the acquired points with respect to $`\{x_s,y_s,z_s\}`$ would be of considerable interest, e.g. for further numerical computations. From a mathematical standpoint, a change of reference frame, i.e. change of basis, from $`\{x,y,z\}`$ to $`\{x_s,y_s,z_s\}`$ is sought as well as the associated matrix.

In this instance, it is therefore evident that finding this matrix by inspection, intuition or 'trial & error' becomes preposterous. SVD holds the potential to compute such a matrix almost automatically. Let $\mathbf{P}\in\mathbb{R}^{N\times 3}$ be the matrix representing the topography. In order to apply the SVD and obtain satisfactory results, the whole point topography should be translated to $-G$ (the centroid). Following, the SVD applied to $\mathbf{P}$ provides $\mathbf{U}\in \mathbb{R}^{N\times N}$ , $\mathbf{S}\in \mathbb{R}^{N\times 3}$, and $\mathbf{V}^\top\in \mathbb{R}^{3\times 3}$. In particular, $\mathbf{V}$ realises the change of basis. Hence, each point of $\mathbf{P}$, namely $\mathbf{p}_i$, will be rotated according to $\mathbf{V}^\top$:

$$\mathbf{p}_i \leftarrow \mathbf{V}^\top\mathbf{p}_i.$$

To perform this, just invoke:

```python
t.rotate_by_svd()
```

which wraps `rotate_about_centre`.

Since $\mathbf{V}^\top$ is orthonormal, the topography is subjected to a *rigid* rotation: neither stretching nor deformations occur. If the $\det{V^\top} \simeq -1$, the transformed topography (through SVD) may need flipping. To overcome this, just use ```flip``` ([Flip](#flip)).

# Data Regression <a name="data-regression"></a>

## Gaussian Process Regression (GPR) <a name="gpr"></a>

PyToM-3D wraps the regressors of scikit-learn, amongst which GPR. In this instance, the topography is modelled as the following regression model:

$$t(x,y) = f(x,y) + \epsilon(0, \sigma),$$

where $f(x,y)$ is a latent function modelling the topography and $\epsilon$ is Gaussian noise having null mean and $\sigma$ as the standard deviation. Next, a Gaussian Process is placed over $f(x,y)$:

$$f \sim GP(M(x,y), K(x,y)),$$

where $M(x,y)$ is the mean, and $K(x,y)$ is the kernel (covariance function). Initially, we need to define the kernel:

```python
from sklearn.gaussian_process.kernels import RBF, WhiteKernel, ConstantKernel
kernel = ConstantKernel() * RBF([1.0, 1.0], (1e-5, 1e5)) \
+ WhiteKernel(noise_level=1e-3, noise_level_bounds=(1e-5, 1e5))
```

which represents a typical squared exponential kernel with noise:

$$K(\mathbf{x},\mathbf{x}') = C \exp{\left(\frac{\Vert \mathbf{x} - \mathbf{x}'\Vert^2}{l^2}\right)} + \sigma_{ij},$$

where:

$$\sigma_{ij} = \delta_{ij} \epsilon,\quad \mathbf{x}=\left[x,y\right]$$

and $\delta_{ij}$ is Kronecker's delta applied to any pair of points $\mathbf{x}_{i}$, $\mathbf{x}_j$.

Finally, we invoke:

```python
from sklearn.gaussian_process import GaussianProcessRegressor as gpr
t.fit(gpr(kernel=kernel))
```

and the GPR is fit. To predict data:

```python
t.pred(X)
```

where `X` is $M \times 2$ a numpy array containing the $x$ and $y$ coordinates of an evaluation grid.

## Spline Fitting <a name="spline"></a>

PyToM-3D allows user to fit data by bi-variate splines specifically wrapping `scipy.interpolate.bisplrep`. Modelling the topography as a two-variable function $f(x,y)$, we approximate it as:

$$f(x,y) \approx \sum_{i=1}^{m} \sum_{j=1}^{n} C_{ij} Q_{i,r}(x) Q_{j,s}(y), $$

where $Q_{i,r}$ and $Q_{j,s}$ are polynomials of degree $r$ and $s$ respectively. Additionally, $m$ and $n$ are the number of control points (aka knots) distributed along the $x$- and $y$-axis. Lastly, $C_{ij}$ are the trainable coefficients, which are determined upon the points of the topography.

To fit a bi-variate spline to the data, we call:

```python
from scipy.interpolate import bisplrep
t.fit(bisplrep, kx, ky, tx, ty)
```

where `kx`, `ky` represent the above-mentioned $r$ and $s$ (degree of the polynomials), and `tx` and `ty` are the control points, which are expected to be `np.ndarray`-like objects. Once the fitting is done we forecast the topography elsewhere by:

```python
t.pred(X)
```

# Data Inspection <a name="data-inspection"></a>
PyToM-3D offers afew visualisation utilities to inspect the data. Particularly, it exploits `pytom3d.Viewer.Viewer` class. So we need one istance of this class before proceeding:

```python
from pytom3d.Viewer import Viewer
v = Viewer()
``` bash
pip install pytom3d
```

## 2D Views <a name="2d"></a>
## Resources

We can easily display the three Cartesian views of the topography `t`, by using:

```python
v.views2D(t)
```

The method can take an indefinite number of input topographies.

## 3D Scatter <a name="3d"></a>

Similarly we can easily the 3D scatter plot of `t` with:

```python
v.scatter(t)
```
- **Package**. [PyPI](https://pypi.org/project/pytom3d/)

The method accepts an indefinite number of input topographies.
- **Documentation**. [ReadTheDocs](https://pytom-3d.readthedocs.io/en/latest/)
7 changes: 7 additions & 0 deletions buildPackageAndDocs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pip3 uninstall pytom3d -y
python3 -m build
pip3 install dist/pytom3d-0.0.0rc0-py3-none-any.whl
sphinx-apidoc -o ./docs/ ./src/
cd docs/
make clean & make html
cd ..
2 changes: 1 addition & 1 deletion createVirtualEnv.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

# Set the path to your .whl file
WHL_FILE=./dist/pytom3d-0.0.0-py3-none-any.whl
WHL_FILE=./dist/pytom3d-0.0.2-py3-none-any.whl

# Set the desired path for the virtual environment
VENV_DIR=$HOME/pythonEnv/simulation
Expand Down
File renamed without changes.
File renamed without changes.
Binary file added dist/pytom3d-0.0.0rc0-py3-none-any.whl
Binary file not shown.
Binary file added dist/pytom3d-0.0.0rc0.tar.gz
Binary file not shown.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 5080ba4

Please sign in to comment.