<p align="center">

</p>

## Spatial Data Analytics and Machine Learning

### Synthetic 2D Spatial Truth Model and Sample Generator

#### Michael Pyrcz, Associate Professor, University of Texas at Austin

##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

## Synthetic Dataset Generator

This is a synthetic dataset generator to support education with demonstrations, and research and development reasonable synthetic, truth, spatial datasets.

* **reasonable** - honor target statistics including the univariate distribution (histogram) and spatial continuity (variogram)

* **truth** - exhaustive representation (saampling) of the population over the area of interest available at the resolution required (and size) and without error.  Access to truth is valuable for 'validation' of inferential workflows working with a realistically, limited sample of the population

* **spatial** - representing a 2D interval of space

To simulate the typical workflows with sparse sampling of the subsurface we need realistic sample sets the include the following aspects:

* **regular sampling** - in many cases subsurface sampling has a nominal spacing related to subsurface access / infrastructure and subsurface periodicity

* **unstructure / random sampling** - while subsurface sampling is not likely random, with the omission of the various spatial features for citing samples and specifically in the case of low spatial information content (e.g. low correlation between exhaustive features such as seismic attributes and the feature of interest) sample structure may be approximated by a random process

* **sampling bias** - representative sampling does not generally occur.  This would require:(1) **random sampling**, a uniform probability to sample all possible locations, or (2) **regular sampling**, uniform sampling interval.  To impose a realistic level of sampling bias, a rejection sampler is applied as a post-processing step to preferential retain low or high values

#### Getting Started

Here's the steps to get setup in Python with the GeostatsPy package:

1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/).
2. From Anaconda Navigator (within Anaconda3 group), go to the environment tab, click on base (root) green arrow and open a terminal.
3. In the terminal type: pip install geostatspy.
4. Open Jupyter and in the top block get started by copy and pasting the code block below from this Jupyter Notebook to start using the geostatspy functionality.

There are examples below with GeostatsPy functions. You can go here to see a list of the available functions,[GeostatsPy](https://git.io/fh4eX). I have posted a lot of other example workflows and source code to help you get learn about and get work done with **Spatial Data Analytics, Geostatistics and Machine Learning** in Python and R. Check out the [GeostatsGuy Inventory](https://github.com/GeostatsGuy/Resources).

#### Load the Required Packages

We will also need some standard packages. These should have been installed with Anaconda 3.

```python
import geostatspy.geostats as geostats    # reimplimentation of GSLIB algorithms in Python
import geostatspy.GSLIB as GSLIB          # GSLIB visualization, subroutines and algorithm wrappers
import numpy as np                        # ndarrys for gridded data
import pandas as pd                       # DataFrames for tabular data
import matplotlib.pyplot as plt           # for plotting
from copy import copy                     # deep copies
import os                                 # import os to interact with the operating system
```

#### Declare Functions

I coded up the following functions to sample from the truth model.  These include the following operations:

**regular_sample**

Function for regular sampling from an exhaustive truth model, a 2D ndarray

* **array** - 2D ndarray with the truth model
* **xmin** - minimum x coordinate - truth model extents
* **xmax** - maximum x coordinate
* **ymin** - minimum y coordinate
* **ymax** - maximum y coordinate
* **spacing** - spacing of the samples in x and y
* **minsx** - minimum x coordinate of the samples - sampling window, defaults to extend of the array
* **maxsx** - maximum x coordinate of the samples
* **minsy** - minimum y coordinate of the samples
* **maxsy** - maximum y coordinate of the samples
* **name** - feature name

**random_sample**

Funciton for random sample from an exhaustive truth model, a 2D ndarray

* **array** - 2D ndarray with the truth model
* **xmin** - minimum x coordinate - truth model extents
* **xmax** - maximum x coordinate
* **ymin** - minimum y coordinate
* **ymax** - maximum y coordinate
* **nsamp** - number of random samples
* **minsx** - minimum x coordinate of the samples - sampling window, defaults to extend of the array
* **maxsx** - maximum x coordinate of the samples
* **minsy** - minimum y coordinate of the samples
* **maxsy** - maximum y coordinate of the samples
* **name** - feature name

**rejection_sample**

Function to preferentially remove high/low samples from a DataFrame to impose sampling bias

* **df** - DataFrame with the sample data
* **vcol** - feature of interest
* **frac** - fraction of dataset to remove with the rejection sampler
* **wt_min** - weight for the minimum value - linear interpolation used to assign weights from min to max value
* **wt_max** - weight for the maximum value

Here's the functions:

```python
def regular_sample(array,xmin,xmax,ymin,ymax,spacing,minsx=xmin,maxsx=xmax,minsy=ymin,maxsy=ymax,name='Value'):
    x = []; y = []; v = []
    nx = array.shape[1]; ny = array.shape[0]
    xsiz = (xmax-xmin)/nx; ysiz = (ymax-ymin)/ny
    xmn = xmin + 0.5*xsiz; ymn = ymin + 0.5*ysiz
    xx, yy = np.meshgrid(np.arange(xmin, xmax, spacing), np.arange(ymax, ymin, -1 * spacing))
    xx = xx + spacing*0.5; yy = yy - spacing*0.5
    for ix,iy in np.ndindex(xx.shape):
        iix = geostats.getindex(nx,xmn,xsiz,xx[iy,ix])
        iiy = geostats.getindex(ny,ymn,ysiz,yy[iy,ix])
        if yy[iy,ix] >= minsy and yy[iy,ix] <= maxsy:
            if xx[iy,ix] >= minsx and xx[iy,ix] >= minsx:
                x.append(xx[iy, ix])
                y.append(yy[iy, ix])
                v.append(array[ny - iiy - 1, iix])
    df = pd.DataFrame(np.c_[x, y, v], columns=["X", "Y", name])
    return df

def random_sample(array,xmin,xmax,ymin,ymax,nsamp=10,minsx=xmin,maxsx=xmax,minsy=ymin,maxsy=ymax,name='Value'):
    x = []; y = []; v = []
    ny, nx = array.shape
    xx, yy = np.meshgrid(
        np.arange(xmin, xmax,(xmax-xmin)/float(nx)), np.arange(ymax - 1, ymin - 1, -1 * (ymax-ymin)/float(ny))
    )
    mask = np.zeros([ny,nx])
    for iy in range(0,ny):
        for ix in range(0,nx):
            if xx[iy,ix] >= minsx and xx[iy,ix] <= maxsx:
                if yy[iy,ix] >= minsy and yy[iy,ix] <= maxsy:
                    mask[iy,ix] = 1.0

    if nsamp*1.2 > np.sum(mask):
        print('ERROR - too few locations available for number of samples requested!')
        return pd.DataFrame()
    isamp = 0
    while isamp < nsamp:
        sample_index = np.random.choice(range(nx * ny), 1)
        iy = int(sample_index[0] / ny)
        ix = sample_index[0] - iy * nx
        if mask[iy,ix] == 1:
            if xx[iy,ix] >= xmin and xx[iy,ix] <= xmax:
                if yy[iy,ix] >= ymin and yy[iy,ix] <= ymax:
                    x.append(xx[iy, ix])
                    y.append(yy[iy, ix])
                    v.append(array[iy, ix])
                    mask[iy,ix] = 0.0
                    isamp = isamp + 1
    df = pd.DataFrame(np.c_[x, y, v], columns=["X", "Y", name])
    return df

def rejection_sample(df,vcol,frac,wt_min,wt_max):
    value = [np.min(df[vcol].values),np.max(df[vcol].values)]
    wt = [wt_min,wt_max]
    df_copy = df.copy(deep = True)
    df_copy['weights'] = np.interp(df[vcol],value,wt)
    df_sample = df_copy.sample(frac = frac, replace = False, weights = 'weights')
    return df_sample
```

#### Make the Truth Model

We will use the common geostatistical method for simulation, known as sequential Gaussian simulation. This simulation method may be conditioned to:

* honor data at data locations
* honor the global univariate distribution
* honor the variogram

The GeostatsPy method was reimplimented in Python from the original GSLIB code (Pyrcz, M.J.), credit to the authors of GSLIB (Deutsch and Journal, 1998).

We will assume:

* **unconditional / no local data** - the simulation approach actually requires input data, so will will make and input a dummy dataset well outside the model space

* **Gaussian distribution** - the output is standard Gaussian distributed (mean = 0.0 and variance = 1.0) and that will be transformed to a new mean and standard deviation with the affine correction:

\begin{equation}
z_{new} = \frac{ \sigma_{z_{new}} }{ \sigma_{z_{old}} } (z_{old} - \overline{z}_{old} ) + \overline{z}_{new}
\end{equation}

```python
# Truth Model Parameters
seed = 73073                              # random number seed for repeatable random samples and rejection sampler
name = 'Por'                              # name of the feature of interest
mean = 0.13                               # univariate distribution mean - assuming Gaussian distribution
stdev = 0.03                              # univariate standard deviation
nug = 0.0                                 # nugget effect - variogram model
it = 1                                    # type of structure (1 -spherical, 2 - exponential, 3 - Gaussian)
azi = 45.0                                # primary direction/azimuth of continuity (0 = y positive, 90 = x positive)
hmaj = 800                                # variogram range in the major direction
hmin = 300                                # variogram range in the minor direction (major direction + 90)

# Make the variogram object
vario = GSLIB.make_variogram(nug,nst=1,it1=it,cc1=1.0,azi1=azi,hmaj1=hmaj,hmin1=hmin) # make model object

# Make the dummy dataset (should be outside the range of correlation of the model)
df = pd.DataFrame({'X':np.full(100,-9999),'Y':np.full(100,-9999),name:np.random.normal(0.0,1.0,100)})

# Truth Model Grid Parameters
nx = 100; ny = 100                        # number of cells in the x and y directions
xsiz = 10.0; ysiz = 10.0                  # size of the cells in the x and y directions
xmn = xsiz * 0.5; ymn = ysiz * 0.5        # assume origin at 0,0, calculate the 1 cell centroid
xmin = xmn - 0.5*xsiz; ymin = ymn - 0.5*ysiz # assume origin at 0,0, calculate the min and max x/y coordinates
xmax = xmin + nx * xsiz; ymax = ymin + ny * ysiz;
vmin = 0.0; vmax = 0.25                   # feature min and max for color bars

# Calculate the Simulated Truth Model over the Specified Grid and Variogram
sim_ns = geostats.sgsim(df,'X','Y',name,wcol=-1,scol=-1,tmin=-9999,tmax=9999,itrans=0,ismooth=0,dftrans=0,tcol=0,
            twtcol=0,zmin=-3.0,zmax=3.0,ltail=1,ltpar=-3.0,utail=1,utpar=3.0,nsim=1,
            nx=nx,xmn=xmn,xsiz=xsiz,ny=ny,ymn=ymn,ysiz=ysiz,seed=73073,
            ndmin=0,ndmax=100,nodmax=10,mults=0,nmult=2,noct=-1,radius=hmaj,radius1=1,sang1=0,
            mxctx=10,mxcty=10,ktype=0,colocorr=0.0,sec_map=0,vario=vario)

# Apply Affine Correction to adjust the standard normal distribution to the target mean and standard deviation
sim = GSLIB.affine(sim_ns,.12,0.03)
cmap = plt.cm.inferno

# Visualize the Truth Model Map and Histogram
plt.subplot(121)
plt.xlabel('X(m)'); plt.ylabel('Y(m)')
plt.title('Truth Model')
palette.set_under('r', 0.0)
palette.set_over('r', 0.0)
plt.imshow(sim,interpolation = None,extent = [xmin,xmax,ymin,ymax], vmin = vmin, vmax = vmax,cmap = cmap)
cbar = plt.colorbar()
cbar.set_label(name)

plt.subplot(122)
plt.hist(sim.flatten(),bins = np.linspace(vmin,vmax,20),alpha=0.2,color="red",edgecolor="black")
plt.xlim([vmin,vmax])
plt.title('Truth Model Distribution')
plt.xlabel(name); plt.ylabel('Frequency')

plt.subplots_adjust(left=0.0, bottom=0.0, right=1.5, top=0.8, wspace=0.3, hspace=0.3)
plt.show()
```

#### Sample from the Truth Model

The following sampling workflow includes the following steps:

1. Apply Regular Sampling
2. Apply Random Sampling
3. Combine the Regular and Random Samples
4. Apply Rejection Sampling to Impose Bias
5. Remove Spatial Duplicates

We check for and remove spatial duplicates, while each step will not produce duplicates (samples coincidental in space), this is a low probability of this occuring between steps.

* spatial duplicates cause analysis issues such as singular matrices for kriging etc.

You can adjust the following parameters to customize your sample set:

* **spacing** - the interwell spacing in x and y of the regular samples
* **nrandom_sample** - the number of random samples
* **wt_min** - the relative weight of the lowest value - linear interpolation is applied between min and max values
* **wt_max** - the relative weight of the highest value
* **bias_frac** -the proportion of the dataset to reject

Some other ideas:

* limit the regular and random samples to subsets of the truth model (use minsx,maxsx,minsy,maxsy)
* use multiple regular sample sets over different parts of the truth model
* use multiple random sample sets over different parts of the truth model with different sampling densities
* change the random number seed to get multiple datasets with the same sampling parameters

With the provided operations there is a lot of flexibility to build a unique sample set. The following workflow is a simple demonstration:

```python
# Data Set Sampling Paramters
spacing = 30                              # spacing of regular samples
nrandom_sample = 400                      # number of random samples
wt_min = 0.1; wt_max = 1.0                # weigths of minimum and maximum feature values - control on degree of bias
bias_frac = 0.7                           # fration of dataset to remove with rejection sampler
seed = 73073                              # random number seed

np.random.seed(seed = seed)               # set the random number seed for repeatability of samples

# Regular Sampling
regular_samples = regular_sample(sim,xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax,spacing=spacing,
                         minsx=xmin,maxsx=xmax,minsy=ymin,maxsy=ymax,name=name)

# Random Sampling
random_samples = random_sample(sim,xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax,
                                     nsamp = nrandom_sample,minsx=xmin,maxsx=xmax,minsy=ymin,maxsy=ymax,name = name)

# Concatenate (Combine) Regular and Random Samples into One DataFrame
samples = pd.concat([regular_samples,random_samples])

# Rejection Sampler
biased_samples = rejection_sample(samples,name,1-bias_frac,wt_min,wt_max)

# Remove Dupicates
biased_samples = biased_samples[np.invert(biased_samples.duplicated(subset=['X','Y'],keep='first'))]

# Visualize Each Step of the Process and the Final Sample Set and Truth Model
plt.subplot(141)
plt.xlabel('X(m)'); plt.ylabel('Y(m)')
plt.title('Regular Samples')
palette = copy(plt.cm.inferno)
palette.set_under('r', 0.0)
palette.set_over('r', 0.0)
plt.imshow(sim,interpolation = None,extent = [xmin,xmax,ymin,ymax], vmin = vmin, vmax = vmax,cmap = palette)
plt.scatter(regular_samples['X'],regular_samples['Y'],c = regular_samples[name],s = 20, cmap = palette,vmin = vmin, vmax = vmax,
                        edgecolors = 'black',label = 'Regular Samples')
plt.xlim([xmin,xmax]); plt.ylim([ymin,ymax])

plt.subplot(142)
plt.xlabel('X(m)'); plt.ylabel('Y(m)')
plt.title('Random Samples')
palette = copy(plt.cm.inferno)
palette.set_under('r', 0.0)
palette.set_over('r', 0.0)
plt.imshow(sim,interpolation = None,extent = [xmin,xmax,ymin,ymax], vmin = vmin, vmax = vmax,cmap = palette)
plt.scatter(random_samples['X'],random_samples['Y'],c = random_samples[name],s = 20, cmap = palette,vmin = vmin, vmax = vmax,
                        edgecolors = 'black',label = 'Random Samples')
plt.xlim([xmin,xmax]); plt.ylim([ymin,ymax])

plt.subplot(143)
plt.xlabel('X(m)'); plt.ylabel('Y(m)')
plt.title('Regular and Random Samples')
palette = copy(plt.cm.inferno)
palette.set_under('r', 0.0)
palette.set_over('r', 0.0)
plt.imshow(sim,interpolation = None,extent = [xmin,xmax,ymin,ymax], vmin = vmin, vmax = vmax,cmap = palette)
plt.scatter(samples['X'],samples['Y'],c = samples[name],s = 20, cmap = palette,vmin = vmin, vmax = vmax,
                        edgecolors = 'black',label = 'Regular and Random Samples')
plt.xlim([xmin,xmax]); plt.ylim([ymin,ymax])

plt.subplot(144)
plt.xlabel('X(m)'); plt.ylabel('Y(m)')
plt.title('Final Biased Samples')
palette = copy(plt.cm.inferno)
palette.set_under('r', 0.0)
palette.set_over('r', 0.0)
plt.imshow(sim,interpolation = None,extent = [xmin,xmax,ymin,ymax], vmin = vmin, vmax = vmax,cmap = palette)
plt.scatter(biased_samples['X'],biased_samples['Y'],c = biased_samples[name],s = 20, cmap = palette,vmin = vmin, vmax = vmax,
                        edgecolors = 'black',label = 'Biased Samples')
plt.xlim([xmin,xmax]); plt.ylim([ymin,ymax])
ax4 = plt.gca()
cbar = plt.colorbar()
cbar.set_label('Truth Values')
ax4.set_aspect('auto')

plt.subplots_adjust(left=0.0, bottom=0.0, right=3.5, top=1.2, wspace=0.1, hspace=0.2)
plt.show()
```

#### Save the Truth Model and Sample Data

Now we finish up by saving the truth model and sample data.

* sample data DataFrame (biased_sample) will be saved to a '.csv'

* truth model (sim) will be saved to a

More information and examples for loading and saving data with [DataFrames](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/PythonDataBasics_DataFrame.ipynb) and [ndarrays](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/PythonDataBasics_ndarrays.ipynb).

We will complete the following steps:

1. set the working directory, target for the saved files

2. save the sample set

3. save the truth model

```python
# Set the Working Directory
os.chdir('D:\Courses')

# Save the Sample Set
biased_samples.to_csv(name + "_sample.csv")
#load_samples_test = pd.read_csv(name + "_sample.csv") # uncomment to test loading back in the saved sample set

# Save the Truth Model
np.savetxt(name + "_truth_map.csv", sim, delimiter=",")
#load_truth_model_test = np.loadtxt(name + "_truth_map.csv", delimiter=",") # uncomment to test loading back in the saved truth model
```

#### Comments

This was a basic workflow for generating a 2D spatial truth model and an associated sample set. I hope this is useful for education, demonstrations and research.

I have other demonstrations on the basics of working with DataFrames, ndarrays, univariate statistics, plotting data, declustering, data transformations, trend modeling and many other workflows available at https://github.com/GeostatsGuy/PythonNumericalDemos and https://github.com/GeostatsGuy/GeostatsPy.

I hope this was helpful,

*Michael*

#### The Author:

### Michael Pyrcz, Associate Professor, University of Texas at Austin
*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*

With over 17 years of experience in subsurface consulting, research and development, Michael has returned to academia driven by his passion for teaching and enthusiasm for enhancing engineers' and geoscientists' impact in subsurface resource development.

For more about Michael check out these links:

#### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

#### Want to Work Together?

I hope this content is helpful to those that want to learn more about subsurface modeling, data analytics and machine learning. Students and working professionals are welcome to participate.

* Want to invite me to visit your company for training, mentoring, project review, workflow design and / or consulting? I'd be happy to drop by and work with you!

* Interested in partnering, supporting my graduate student research or my Subsurface Data Analytics and Machine Learning consortium (co-PIs including Profs. Foster, Torres-Verdin and van Oort)? My research combines data analytics, stochastic modeling and machine learning theory with practice to develop novel methods and workflows to add value. We are solving challenging subsurface problems!

* I can be reached at mpyrcz@austin.utexas.edu.

I'm always happy to discuss,

*Michael*

Michael Pyrcz, Ph.D., P.Eng. Associate Professor The Hildebrand Department of Petroleum and Geosystems Engineering, Bureau of Economic Geology, The Jackson School of Geosciences, The University of Texas at Austin

I have other demonstrations on the basics of working with DataFrames, ndarrays, univariate statistics, plotting data, declustering, data transformations, trend modeling and many other workflows available at https://github.com/GeostatsGuy/PythonNumericalDemos and https://github.com/GeostatsGuy/GeostatsPy. Also, I have posted a lot of other example workflows and source code to help you get learn about and get work done with **Spatial Data Analytics, Geostatistics and Machine Learning** in Python and R. Check out the [GeostatsGuy Inventory](https://github.com/GeostatsGuy/Resources) and I have a [YouTube](https://youtube.com/GeostatsGuyLectures) channel with all my university lectures recorded.

I hope this was helpful,

*Michael*

Michael Pyrcz, Ph.D., P.Eng. Associate Professor The Hildebrand Department of Petroleum and Geosystems Engineering, Bureau of Economic Geology, The Jackson School of Geosciences, The University of Texas at Austin

#### More Resources Available at: [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

```python

```


```
%matplotlib inline
from IPython.display import HTML
HTML(open("00_custom.css", "r").read())
```

<style>
@font-face {
    font-family: 'Charis SIL';
    src: url('fonts/CharisSILEur-R.woff') format('woff');
}
@font-face {
    font-family: 'Architects Daughter';
    font-style: normal;
    src: url(https://fonts.gstatic.com/s/architectsdaughter/v6/RXTgOOQ9AAtaVOHxx0IUBM3t7GjCYufj5TXV5VnA2p8.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
.prompt{display: None;}
div.cell{margin: auto;width:900px;}

div #notebook { /* centre the content */
    background: #fff9f0;
    margin: auto;
    padding: 0em;
    padding-top: 1em;
}
div #notebook_container {width: 960px!important;}
#notebook li { /* More space between bullet points */
    margin-top:0.2em;
}

div.text_cell_render{
    font-family: "Charis SIL", Cambria,  serif;
    line-height: 155%;
    font-size: 130%;
}
.CodeMirror{
    font-family: Consolas, monospace;
    font-size: 140%;
    background-color:#fffcf6!important;
}

.output_area {
    font-family: Consolas,monospace;font-size: 120%;
    background-color:#fffcf6!important;
    margin-top:0.8em;
}

div.output_latex{overflow:hidden}

h1 {
    font-family: 'Architects Daughter', serif;
    font-size:   48pt!important;
    text-align:  center;
    text-shadow: 4px 4px 4px #aaa;
    padding-bottom: 48pt;
}

.warning{color: rgb( 240, 20, 20 )}
</style>

# Multiple _DOF_ System

```
%matplotlib inline

import numpy as np
from numpy import poly1d as p, polyint as i
from scipy.linalg import eigh
from IPython.display import Latex, display
import matplotlib.pyplot as plt
plt.style.use(['fivethirtyeight', './00_mplrc'])

def P(a,b): return p((a,b))
def print_mat(mat, dl='b', fmt='%f', nl='\\\\'):
    return (r'\begin{'+dl+'matrix}' +
    nl.join('&'.join(fmt%n for n in row) for row in mat) +
    r'\end{'+dl+'matrix}')
```

## Stiffness and Mass matrices, Influence matrix

We remove a constraint, permitting the vertical displacement of the support in $C$, then we compute the reactions and the ending moments in equilibrium, in turns, with a unit force applied correspondingly a DOF.

---

We will need

- a list `ls` with the lengths of the intervals and
- a list of lists `ms`, one list for each unit loading containing the polynomial representation of the bending moments.

```
ls = (1, 2, 2, 2, 2, 2, 1)
ms = [[P(-2,0),P(0,-2),P(1,-2),P(0,0),P(1,0),P(1,2),P(4,0)],
      [P( 0,0),P(0, 0),P(0, 0),P(0,0),P(1,0),P(0,2),P(2,0)],
      [P( 2,0),P(0, 2),P(0, 2),P(1,0),P(1,0),P(1,2),P(4,0)]]
```

Just for peace of conscience, let's print the bending moments.

```
for nf, force in enumerate(ms, 1):
    for ni, interval in enumerate(force, 1):
        print ("M_%d, x_%d: %s" % (nf, ni, interval)).replace('\n','  ')
    if nf<3 : print
```

    M_1, x_1:    -2 x
    M_1, x_2:    -2
    M_1, x_3:    1 x - 2
    M_1, x_4:    0
    M_1, x_5:    1 x
    M_1, x_6:    1 x + 2
    M_1, x_7:    4 x

    M_2, x_1:    0
    M_2, x_2:    0
    M_2, x_3:    0
    M_2, x_4:    0
    M_2, x_5:    1 x
    M_2, x_6:    2
    M_2, x_7:    2 x

    M_3, x_1:    2 x
    M_3, x_2:    2
    M_3, x_3:    2
    M_3, x_4:    1 x
    M_3, x_5:    1 x
    M_3, x_6:    1 x + 2
    M_3, x_7:    4 x

Let's compute the flexibility coefficients of the 3 DOF system... knowing already the results I know that it is convenient to multiply each term by `3`

```
for n1 in (0, 1, 2):
    for n2 in (0, 1, 2):
        print n1+1, n2+1,
        print 3*sum(i(m[0]*m[1])(ls[k])
                    for k,m in enumerate(zip(ms[n1],ms[n2])))
```

    1 1 116.0
    1 2 52.0
    1 3 40.0
    2 1 52.0
    2 2 36.0
    2 3 52.0
    3 1 40.0
    3 2 52.0
    3 3 140.0

From the previous results, we can define the flexibility matrix (note that the terms listed in parentheses are eventually divided by `3`),

```
F = np.array(((116,52,40),
              (52,36,52),
              (40,52,140)))/3.
Latex(r'$$F=\frac{L^3}{3EJ}' +
      print_mat(F*3., fmt='%05.1f') + '$$')
```

$$F=\frac{L^3}{3EJ}\begin{bmatrix}116.0&052.0&040.0\\052.0&036.0&052.0\\040.0&052.0&140.0\end{bmatrix}$$

The stiffness matrix follows immediately by inversion

```
K = np.linalg.inv(F)
Latex(r'$$K=\frac{3EJ}{3196L^3}' +
      print_mat(K*3196./3., fmt='%+08.3f') + '$$')
```

$$K=\frac{3EJ}{3196L^3}\begin{bmatrix}+146.000&-325.000&+079.000\\-325.000&+915.000&-247.000\\+079.000&-247.000&+092.000\end{bmatrix}$$

The 3x3 stiffness matrix must be partitioned accordingly to the structural degrees of freedom and the DOF associated with the imposed displacement.

```
Kss = K[:2,:2] ; Ksg = K[:2,2:]
Kgs = K[2:,:2] ; Kgg = K[2:,2:]
Mss = np.array((((1.0, 0.0),
                 (0.0, 1.0))))
display(Latex('$$K_{ss} =\\frac{3EJ}{3196L^3}' +
              print_mat(Kss*3196./3.) + '$$'))
display(Latex('$$M_{ss} = m' +
              print_mat(Mss) + '$$'))

```

$$K_{ss} =\frac{3EJ}{3196L^3}\begin{bmatrix}146.000000&-325.000000\\-325.000000&915.000000\end{bmatrix}$$

$$M_{ss} = m\begin{bmatrix}1.000000&0.000000\\0.000000&1.000000\end{bmatrix}$$

The influence matrix is simply $E = - K_{ss}^{-1}\,K_{sg}$

```
E = -np.dot(np.linalg.inv(Kss), Ksg)
Latex('$$E=\\frac1{35}'+print_mat(35*E)+'$$')
```

$$E=\frac1{35}\begin{bmatrix}10.000000\\13.000000\end{bmatrix}$$

## Eigenvalues and Eigenvectors

We represent the eigenvalues as multiples of a reference squared frequency $\omega_0^2$.

```
evl, evc = eigh(Kss, Mss)
display(Latex(r'$$\omega_0^2 = \frac{EJ}{mL^3}.$$'))
display(Latex(r'$$\omega_1^2 = %f\,\omega_0^2,\quad'%evl[0] +
              r'\omega_2^2 = %f\,\omega_0^2.$$'%evl[1]))
Latex(r'$$\psi_1 = ' + print_mat(evc[:,0,None], dl='B') + r',\quad' +
      r'  \psi_2 = ' + print_mat(evc[:,1,None], dl='B', fmt='%+f') + r'.$$')
```

$$\omega_0^2 = \frac{EJ}{mL^3}.$$

$$\omega_1^2 = 0.025388\,\omega_0^2,\quad\omega_2^2 = 0.970544\,\omega_0^2.$$

$$\psi_1 = \begin{Bmatrix}-0.939075\\-0.343711\end{Bmatrix},\quad  \psi_2 = \begin{Bmatrix}-0.343711\\+0.939075\end{Bmatrix}.$$

## Modal Equations of motion

The equations of motion can be written in terms of the structural deflections $\boldsymbol x$

$$M\ddot{x} + Kx = -ME\ddot{v}_c = -mE\ddot{v}_c(t)$$

Introducing the modal coordinates $\boldsymbol q$, from $x = \Psi q$ and premultiplying term by term by $\Psi^T$ we have

$$ m \ddot{q}_i + m \Lambda_i^2\omega_o^2 q_i = -m\, \psi_i^TE\,\ddot{v}_c(t),\qquad i=1,2.$$

Note that we can simplify the unit mass $m$ from the above equation.

```
print (r'\begin{align}'+
       r'\\'.join(r'\ddot{q}_%d + %f\omega_0^2q_%d &= %+f\,\ddot{v}_c(t)'%
                 (i+1, evl[i], i+1, -np.dot(evc.T,E)[i,0])
                 for i in (0,1)) + r'\end{align}')
```

    \begin{align}\ddot{q}_1 + 0.025388\omega_0^2q_1 &= +0.395971\,\ddot{v}_c(t)\\\ddot{q}_2 + 0.970544\omega_0^2q_2 &= -0.250596\,\ddot{v}_c(t)\end{align}

```
Latex(r'\begin{align}'+
      r'\\'.join(r'\ddot{q}_%d + %f\omega_0^2q_%d &= %+f\,\ddot{v}_c(t)'%
                 (i+1, evl[i], i+1, -np.dot(evc.T,E)[i,0])
                 for i in (0,1)) + r'\end{align}')
```

\begin{align}\ddot{q}_1 + 0.025388\omega_0^2q_1 &= +0.395971\,\ddot{v}_c(t)\\\ddot{q}_2 + 0.970544\omega_0^2q_2 &= -0.250596\,\ddot{v}_c(t)\end{align}

### Support Motion

We have $v_c=v_c(\tau)$, so we have to see what happens when we write it as a function of $t$, but we want to start by plotting $v,\;\dot v$ and $\ddot v$ as a function of $\tau$. First, define our functions

```
def f0(tau): return np.where(tau<=0.25, tau**3*(tau*(tau*6144 - 3840) + 640), 1)
def f1(tau): return np.where(tau<=0.25, tau**2*(tau*(tau*30720 - 15360) + 1920), 0)
def f2(tau): return np.where(tau<=0.25, tau**1*(tau*(tau*122880 - 46080) + 3840), 0)
```

then we plot the support displacement, the support velocity and the support acceleration (note that we applied different scale factors to have the different functions in the same plot, and note that deriving with respect to $\tau$ is different than deriving with respect to $t$).

```
t = np.linspace(0.0, 0.4, 1001)
plt.plot(t, f0(t),     label='$x_c$')
plt.plot(t, f1(t)/8,   label='$v_c/8$')
plt.plot(t, f2(t)/100, label='$a_c/100$')
plt.legend(loc=4)
plt.xlim((0,0.40))
plt.ylim((-1.05,1.05))
plt.grid(1);
```

```
def particular(m, k, c1, c2, c3, tmax):
    d = c3 / k
    c = c2 / k
    b = c1 / k - 6*(c3*m) / k**2
    a = -2*(c2*m) / k**2
    def xi(t): return  t*(t*(d*t+c)+b)+a if t<= tmax else 0
    d3 = d*3 ; c2 = c*2 ; b1 = b*1 ; a0 =a*0
    def dotxi(t): return t*(d3*t+c2)+b1  if t<= tmax else 0
    return xi, dotxi
```

\begin{align}
  v_c &= \delta\left(640\frac{t^3}{T_1^3} - 3840\frac{t^4}{T_1^4} + 6144\frac{t^5}{T_1^5}\right),\\
  \dot v_c &= \delta\frac1{T_1}\left(1920\frac{t^2}{T_1^2} - 15360\frac{t^3}{T_1^3} + 30720\frac{t^4}{T_1^4}\right),\\
  \ddot v_c &= \delta\frac1{T_1^2}\left(3840 \frac{t}{T_1} - 46080 \frac{t^2}{T_1^2} + 122880 \frac{t^3}{T_1^3}\right).
\end{align}

But
$$\frac1{T_1^2} = \frac{\Lambda_1^2}{(2\pi)^2}\,\omega_0^2$$
and the modal equation of motion is hence
\begin{align}
  m \ddot{q}_i(t) + m \Lambda_i^2\omega_o^2 q_i(t) &= -m\,\delta\, \psi_i^TE\,
  \frac{\Lambda_1^2}{(2\pi)^2}\,\omega_0^2 \left(3840 \frac{t}{T_1} - 46080 \frac{t^2}{T_1^2} + 122880 \frac{t^3}{T_1^3}\right)
\end{align}

Using the adimensional time coordinate $\tau = t/T_1$ and taking into account that

\begin{align}
  \dot f(t) &= \dot f(\tau) d\tau/dt = \dot f(\tau)/T_1 \\
  &= \Lambda_1\omega_0\,\dot f(\tau)/2\pi
\end{align}

eventually we have

\begin{align}
  m \frac{\Lambda_1^2}{(2\pi)^2}\omega_0^2\ddot{q}_i(\tau) + m \Lambda_i^2\omega_o^2 q_i(\tau) &= -m\,\delta\, \psi_i^TE\,
  \frac{\Lambda_1^2}{(2\pi)^2}\,\omega_0^2 \left(3840 \tau - 46080 \tau^2 + 122880 \tau^3\right),
\end{align}

simplifying

\begin{align}
  \ddot{q}_i(\tau) + (2\pi)^2\frac{\Lambda_i^2}{\Lambda_1^2}q_i(\tau) =
  \ddot{q}_i(\tau) + a_i^2q_i(\tau)
  &= -\delta\,\psi_i^TE\,\left(3840 \tau - 46080 \tau^2 + 122880 \tau^3\right),\quad
  a_i = 2\pi\frac{\Lambda_i}{\Lambda_1}.
\end{align}

The integral is

\begin{align}
  q_i(\tau) &= A_i\sin a_i\tau + B_i\cos a_i\tau + \xi_i(\tau),\\
  \dot q_i(\tau) &= a_i A_i \cos a_i\tau - a_i B_i \sin a_i\tau + \dot\xi_i(\tau).
\end{align}

Because the system starts from rest

\begin{align}
  B_i &= -\xi(0),&A_i &= -\dot\xi(0)/a_i.
\end{align}

Eventually, the particular integral is given by

\begin{align}
  \xi_i(\tau) = -\delta\,\psi_i^TE\,\left(
  \frac{92160}{a_i^4} + \frac{3840a_i^2-737280}{a_i^4} \tau -
  \frac{46080}{a_i^2} \tau^2 + \frac{122880}{a_i^2} \tau^3
  \right).
\end{align}

```
X3, X4, X5 = 640, -3840, 6144
V2, V3, V4 = X3*3, X4*4, X5*5
A1, A2, A3 = V2*2, V3*3, V4*4
A0 = 0

tau_01 = np.linspace(0.00, 0.25, 251)[:,None]
tau_12 = np.linspace(0.25, 1.00, 751)[:,None]

a = 2*np.pi*np.sqrt(evl/evl[0])
print "a       =", a
print "a / 2pi =", a/2/np.pi
psiE = evc.T.dot(E).flatten()
print "Psi^T E =", psiE
print -psiE*A3/a**2
print -psiE*A2/a**2
print -psiE*(A1*a**2-6*A3)/a**4
print -psiE*(-2*A2)/a**4
t = tau_01
dxi = A3*t**3/a**2 + A2*t**2/a**2 + t*(A1*a**2-6*A3)/a**4 - 2*A2/a**4
vxi = 3*A3*t**2/a**2 + 2*A2*t/a**2 + (A1*a**2-6*A3)/a**4
axi = 6*A3*t/a**2 + 2*A2/a**2

plt.plot(t, dxi*a**2+axi);
```

```
axi = None
dxi = -dxi*psiE ; vxi = -vxi*psiE
B = -dxi[0] ; A = -vxi[0]/a
dq = A*np.sin(a*t) + B*np.cos(a*t) + dxi
vq = a*(A*np.cos(a*t) - B*np.sin(a*t)) + vxi

t2 = np.linspace(0.00, 0.75, 751)[:,None]
B2 = dq[-1] ; A2 = vq[-1]/a
dq2 = A2*np.sin(a*t2) + B2*np.cos(a*t2)
vq2 = a*(A2*np.cos(a*t2)-B2*np.sin(a*t2))
```

```
T = np.vstack((t,t2+0.25))
Q = np.vstack((dq,dq2))
plt.plot(T, Q[:,0], label="$q_1(\\tau)$")
plt.plot(T, Q[:,1], label="$q_2(\\tau)$")
plt.legend(loc=1)
plt.xticks(np.linspace(0,1,9));
```

```
X = evc.dot(Q.T).T
plt.plot(T, X);
```


# A Python Module for Mobility Determination of Parallel Manipulators Using Screw Theory

### Adriyan

**Department of Mechanical Engineering**
**Sekolah Tinggi Teknologi Nasional, Jambi, 36125, Indonesia**

© Adriyan, 2019

---

This material is used as a part of a research article which entitled A Python Module for Mobility Determination of Parallel Manipulators Using Screw Theory which published in Journal METAL, Andalas University.

Material appears in this notebook is released under 3-clause BSD License.

**If you found this notebook and/or the pyScrew4Mobility package is worth for your research or publications please cites the paper.**

---

```python
from IPython.display import HTML, display_png
# with open("jlstyle.css", "r") as fs: fs=fs.read() # If you run on Jupyter Lab
with open("nbstyle.css", "r") as fs: fs=fs.read() # If you run on Jupyter Notebook
HTML(fs)
```

## 1. A Grübler-Kutzbach Mobility

Grübler-Kutzbach mobility formulas:

$$
M = \kappa \cdot \left( n - j - 1\right) + \sum_{i}{f_i}
$$

where $\kappa$ is equal to 3 and 6 for planar and spatial manipulators, respectively. $n$ and $j$ denote the number of links and joints in the mechanisms/manipulators, respectively. While, $\sum_{i}{f_i}$ is the number of motion allowed by each joint that constructs the mechanisms/manipulators.

```python
def Mobility_GK(n, j, Sigma_fi, kappa=6):
    return kappa * (n - j - 1) + Sigma_fi
```

## 2. `pyScrew4Mobility` Module: the implementation of screw theory for mobility determination of parallel manipulators using sympy

Currently, `pyScrew4Mobility` Module is constructed by three python classes:
1. A class of `Screw` which responsible to construct a screw or a screw system.
2. A class of `ScrewSystem`  which responsible for algebraic calculation of the screw or the screw system, such as orthogonal product and reciprocal screw.
3. A class of `Mobility` which applied to calculate mobility of parallel manipulators for given screw system of its each limb.

The `pyScrew4Mobility Module` is released under 3-Clause BSD (Berkeley Software Disribution) License.

To use `pyScrew4Mobility Module` using this (Jupyter) notebook, one must put the the file `pyScrew4Mobility.py` in the same directory of this notebook and use the IPython magic as given below before importing the screw library.

```python
%load_ext autoreload
%autoreload 2
import pyScrew4Mobility as s4m
```

## 3. The Application of `pyScrew4Mobility` Package to Four Well-known Parallel Manipulators

The `pyScrew4Mobility` package is applied to determine the mobility of four well-known parallel manipulators, namely:
1. **Tripteron: a 3-PRRR parallel manipulator**
2. **A star-like parallel manipulator 3-PR(Pa)R**
3. **A 4-PRRU parallel manipulator**, Quadrupteron (PRRR/3-PRRU) is the special case.
4. **A 6-UPS parallel manipulator**

```python
import sympy as sym
from sympy.physics import mechanics as mec
from sympy.physics import matrices as mat
```

```python
sym.init_printing(use_latex="mathjax")
```

### 3.1. A 3-PRRR Parallel Manipulator

The geometry of manipulator

**Examining the mobility with Grübler-Kutzbach mobility formula**

```python
n = 1 + 3 * 3 + 1
j = 3 * 4
Sigma_fi = 3 * 4
print("Mobility of 3-PRRR using Grübler-Kutzbach mobility formulas: " \
      + str(Mobility_GK(n, j, Sigma_fi)))
```

    Mobility of 3-PRRR using Grübler-Kutzbach mobility formulas: 0

**The zeroth step:** A user defined for ${\bf{r}}_{ij}$, ${\bf{s}}_{ij}$ and $h_{ij}$ of 3-PRRR PM.

```python
def PM_3PRRR():
    # Number of limbs
    n_limbs = 3
    # Declaring sympy symbol for its kinematic parameter
    a, b, Ly, Lz, r = sym.symbols("a b L_y L_z r")
    # Joint displacement and/or joint angular displacement
    act_spc = sym.symbols("d_{1:"+str(n_limbs+1)+"}")
    thetas = sym.symbols("\\theta_{{1:"+str(n_limbs+1)+"}{2:4}}")
    # The unit vector of a unit screw pointing at for each limb
    _s = [4 * [mat.Matrix([1, 0, 0]),],
          4 * [mat.Matrix([0, 1, 0]),],
          4 * [mat.Matrix([0, 0, 1])]]
    # pitch of the each joint for every limb
    _h = [sym.oo,] + 3 * [0,]
    # A fixed reference of frame O-XYZ
    G = mec.ReferenceFrame("G")
    # Position vector of OA_i
    rOA = [mat.Matrix([act_spc[0], Ly, 0]),
           mat.Matrix([0, act_spc[1], Lz]),
           mat.Matrix([0, 0, act_spc[2]])]
    # An auxiliary vector for determining position vector AB and BC
    _ = [mat.Matrix([0, 1, 0]),
         mat.Matrix([0, 0, 1]),
         mat.Matrix([1, 0, 0])]
    # Within this loop, it is constructed all parameters required for
    # mobility calculation
    list_of_parameters = [] # Variable for containing all parameters
                            # required for mobility calculation
    ii = 0
    for _1, _2, _3 in zip(thetas[::2], thetas[1::2], [G.x, G.y, G.z]):
        # Moving reference of frame
        H = G.orientnew("H"+str(ii+1), "Axis", [_1, _3])
        K = G.orientnew("K"+str(ii+1), "Axis", [_2, _3])
        # Position vector AB and BC in fixed reference of frame
        rAB = G.dcm(H) * (a * _[ii])
        rBC = G.dcm(K) * (b * _[ii])
        # Contruct all parameters for each limb
        s_limb = []
        for jj in range(len(_s[ii])):
            _r = rOA[ii]
            if jj == 2:
                _r += rAB
            elif jj == 3:
                _r += rAB + rBC
            s_limb.append([_s[ii][jj], _r, _h[jj]])
        list_of_parameters.append(s_limb)
        ii += 1
    return list_of_parameters

```

**The first to fourth step:**

```python
Mobility_PM_3PRRR = s4m.ManipulatorsMobility(PM_3PRRR(), "A 3-PRRR Parallel Manipulator")
```

```python
Mobility_PM_3PRRR.Mobility
```

    {'Number': 3, 'Type': '3T', 'Direction': ['Tx', 'Ty', 'Tz']}

**Displaying the result from the first to fourth step:**

```python
def all_screw_system(obj):
    print("Limb Twist System")
    print("=================")
    for ii, item in enumerate(obj.LimbTwistSystem):
        print("Limb: ", ii+1)
        display(item.screw_system_list)

    print("\n\nLimb Wrench System")
    print("==================")
    for ii, item in enumerate(obj.LimbWrenchSystem):
        print("Limb: ", ii+1)
        display(item.screw_system_list)

    print("\n\nPlatform Wrench System")
    print("======================")
    display(obj.PlatformWrenchSystem.screw_system_list)

    print("\n\nPlatform Twist System")
    print("=====================")
    display(obj.PlatformTwistSystem.screw_system_list)
```

```python
all_screw_system(Mobility_PM_3PRRR)
```

    Limb Twist System
    =================
    Limb:  1

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}1\\0\\0\\0\\0\\- L_{y}\end{matrix}\right], \  \left[\begin{matrix}1\\0\\0\\0\\a \sin{\left(\theta_{{1}{2}} \right)}\\- L_{y} - a \cos{\left(\theta_{{1}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}1\\0\\0\\0\\a \sin{\left(\theta_{{1}{2}} \right)} + b \sin{\left(\theta_{{1}{3}} \right)}\\- L_{y} - a \cos{\left(\theta_{{1}{2}} \right)} - b \cos{\left(\theta_{{1}{3}} \right)}\end{matrix}\right]\right]$

    Limb:  2

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right], \  \left[\begin{matrix}0\\1\\0\\- L_{z}\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\1\\0\\- L_{z} - a \cos{\left(\theta_{{2}{2}} \right)}\\0\\a \sin{\left(\theta_{{2}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\1\\0\\- L_{z} - a \cos{\left(\theta_{{2}{2}} \right)} - b \cos{\left(\theta_{{2}{3}} \right)}\\0\\a \sin{\left(\theta_{{2}{2}} \right)} + b \sin{\left(\theta_{{2}{3}} \right)}\end{matrix}\right]\right]$

    Limb:  3

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\a \sin{\left(\theta_{{3}{2}} \right)}\\- a \cos{\left(\theta_{{3}{2}} \right)}\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\a \sin{\left(\theta_{{3}{2}} \right)} + b \sin{\left(\theta_{{3}{3}} \right)}\\- a \cos{\left(\theta_{{3}{2}} \right)} - b \cos{\left(\theta_{{3}{3}} \right)}\\0\end{matrix}\right]\right]$

    Limb Wrench System
    ==================
    Limb:  1

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right]\right]$

    Limb:  2

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right]\right]$

    Limb:  3

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right]\right]$

    Platform Wrench System
    ======================

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right]\right]$

    Platform Twist System
    =====================

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right]\right]$

### 3.2. A 3-PR(Pa)R Parallel Manipulator

The geometry of manipulator

**Examining the mobility with Grübler-Kutzbach mobility formula**

```python
n = 1 + 3 * 5 + 1
j = 3 * 7
Sigma_fi = 3 * 7
print("Mobility of 3-PR(Pa)R using Grübler-Kutzbach mobility formulas: " \
      + str(Mobility_GK(n, j, Sigma_fi)))
```

    Mobility of 3-PR(Pa)R using Grübler-Kutzbach mobility formulas: -9

**The zeroth step:** A user defined for ${\bf{r}}_{ij}$, ${\bf{s}}_{ij}$ and $h_{ij}$ of 3-PR(Pa)R PM.

```python
def PM_3PRPaR():
    # Number of limbs
    n_limbs = 3
    # Declaring sympy symbol for its kinematic parameter
    L, r = sym.symbols("L r")
    a, b = sym.symbols("a b")
    # Joint displacement and/or joint angular displacement
    act_spc = sym.symbols("d_{1:"+str(n_limbs+1)+"}")
    lambdas = sym.symbols("\\lambda_{1:"+str(n_limbs+1)+"}")
    thetas = sym.symbols("\\theta_{{1:"+str(n_limbs+1)+"}{2:6}}")
    lambdas_subs = dict(zip(lambdas, [0, 2*sym.pi/3, 4*sym.pi/3]))
    # A fixed reference of frame O-XYZ
    G = mec.ReferenceFrame("G")
    # Within this loop, it is constructed all parameters required for
    # mobility calculation
    list_of_parameters = [] # Variable for containing all parameters
                            # required for mobility calculation
    kk = len(thetas)//n_limbs
    for ii in range(n_limbs):
        # Moving reference of frame
        H = G.orientnew("H"+str(ii+1), "Axis", [lambdas[ii].subs(lambdas_subs), G.z])
        rOA0 = mat.Matrix([act_spc[ii], 0, 0])
        rOA1 = mat.Matrix([act_spc[ii] - b/2, 0, 0])
        rOA2 = mat.Matrix([act_spc[ii] + b/2, 0, 0])
        K = G.orientnew("K"+str(ii+1), "Axis", [thetas[ii*kk], G.x])
        N = G.orientnew("N"+str(ii+1), "Body", [*thetas[ii*kk:ii*kk+2], 0], "XYZ")
        # Position vector A0B0, A1B1, and A2B2 in fixed reference of frame
        rA0B0, rA1B1, rA2B2 = 3 * [G.dcm(N) * mat.Matrix([L, 0, 0]),]
        # The unit vector of a unit screw pointing at for each limb
        _s = 2 * [mat.Matrix([1, 0, 0]),] \
             + 4 * [mat.Matrix([0, 1, 0]),] \
             + [mat.Matrix([1, 0, 0]),]
        # pitch of the each joint for every limb
        _h = [sym.oo,] + 6 * [0,]
        # Contruct all parameters for each limb
        s_limb = []
        for jj, item in enumerate(_s):
            _r = rOA0
            if jj in [2, 3, 4, 5, 6]:
                item = G.dcm(K) * item
                if jj == 2:
                    _r = rOA1
                elif jj == 3:
                    _r = rOA2
                elif jj == 4:
                    _r = rOA1 + rA1B1
                elif jj == 5:
                    _r = rOA2 + rA2B2
                elif jj == 6:
                    _r = rOA0 + rA0B0
            s_limb.append([G.dcm(H) * item, G.dcm(H) * _r, _h[jj]])
        list_of_parameters.append(s_limb)
    return list_of_parameters

```

**The first to fourth step:**

```python
Mobility_PM_3PRPaR = s4m.ManipulatorsMobility(PM_3PRPaR(), "A 3-PR(Pa)R Parallel Manipulator")
```

```python
Mobility_PM_3PRPaR.Mobility
```

    {'Number': 3, 'Type': '3T', 'Direction': ['Tx', 'Ty', 'Tz']}

**Displaying the result from the first to fourth step:**

```python
all_screw_system(Mobility_PM_3PRPaR)
```

    Limb Twist System
    =================
    Limb:  1

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}1\\0\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\theta_{{1}{2}} \right)}\\\sin{\left(\theta_{{1}{2}} \right)}\\0\\\left(\frac{b}{2} - d_{1}\right) \sin{\left(\theta_{{1}{2}} \right)}\\\left(- \frac{b}{2} + d_{1}\right) \cos{\left(\theta_{{1}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\theta_{{1}{2}} \right)}\\\sin{\left(\theta_{{1}{2}} \right)}\\0\\- \left(\frac{b}{2} + d_{1}\right) \sin{\left(\theta_{{1}{2}} \right)}\\\left(\frac{b}{2} + d_{1}\right) \cos{\left(\theta_{{1}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\theta_{{1}{2}} \right)}\\\sin{\left(\theta_{{1}{2}} \right)}\\L \sin{\left(\theta_{{1}{3}} \right)}\\\left(- L \cos{\left(\theta_{{1}{3}} \right)} + \frac{b}{2} - d_{1}\right) \sin{\left(\theta_{{1}{2}} \right)}\\\left(L \cos{\left(\theta_{{1}{3}} \right)} - \frac{b}{2} + d_{1}\right) \cos{\left(\theta_{{1}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\theta_{{1}{2}} \right)}\\\sin{\left(\theta_{{1}{2}} \right)}\\L \sin{\left(\theta_{{1}{3}} \right)}\\- \left(L \cos{\left(\theta_{{1}{3}} \right)} + \frac{b}{2} + d_{1}\right) \sin{\left(\theta_{{1}{2}} \right)}\\\left(L \cos{\left(\theta_{{1}{3}} \right)} + \frac{b}{2} + d_{1}\right) \cos{\left(\theta_{{1}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}1\\0\\0\\0\\- L \sin{\left(\theta_{{1}{3}} \right)} \cos{\left(\theta_{{1}{2}} \right)}\\- L \sin{\left(\theta_{{1}{2}} \right)} \sin{\left(\theta_{{1}{3}} \right)}\end{matrix}\right]\right]$

    Limb:  2

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\- \frac{1}{2}\\\frac{\sqrt{3}}{2}\\0\end{matrix}\right], \  \left[\begin{matrix}- \frac{1}{2}\\\frac{\sqrt{3}}{2}\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{2}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{2}{2}} \right)}}{2}\\\sin{\left(\theta_{{2}{2}} \right)}\\\frac{\sqrt{3} \left(- b + 2 d_{2}\right) \sin{\left(\theta_{{2}{2}} \right)}}{4}\\\frac{\left(- b + 2 d_{2}\right) \sin{\left(\theta_{{2}{2}} \right)}}{4}\\\left(- \frac{b}{2} + d_{2}\right) \cos{\left(\theta_{{2}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{2}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{2}{2}} \right)}}{2}\\\sin{\left(\theta_{{2}{2}} \right)}\\\frac{\sqrt{3} \left(b + 2 d_{2}\right) \sin{\left(\theta_{{2}{2}} \right)}}{4}\\\frac{\left(b + 2 d_{2}\right) \sin{\left(\theta_{{2}{2}} \right)}}{4}\\\left(\frac{b}{2} + d_{2}\right) \cos{\left(\theta_{{2}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{2}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{2}{2}} \right)}}{2}\\\sin{\left(\theta_{{2}{2}} \right)}\\\frac{\sqrt{3} L \sin{\left(\theta_{{2}{2}} \right)} \cos{\left(\theta_{{2}{3}} \right)}}{2} - \frac{L \sin{\left(\theta_{{2}{3}} \right)}}{2} - \frac{\sqrt{3} b \sin{\left(\theta_{{2}{2}} \right)}}{4} + \frac{\sqrt{3} d_{2} \sin{\left(\theta_{{2}{2}} \right)}}{2}\\\frac{L \sin{\left(\theta_{{2}{2}} \right)} \cos{\left(\theta_{{2}{3}} \right)}}{2} + \frac{\sqrt{3} L \sin{\left(\theta_{{2}{3}} \right)}}{2} - \frac{b \sin{\left(\theta_{{2}{2}} \right)}}{4} + \frac{d_{2} \sin{\left(\theta_{{2}{2}} \right)}}{2}\\\left(L \cos{\left(\theta_{{2}{3}} \right)} - \frac{b}{2} + d_{2}\right) \cos{\left(\theta_{{2}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{2}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{2}{2}} \right)}}{2}\\\sin{\left(\theta_{{2}{2}} \right)}\\\frac{\sqrt{3} L \sin{\left(\theta_{{2}{2}} \right)} \cos{\left(\theta_{{2}{3}} \right)}}{2} - \frac{L \sin{\left(\theta_{{2}{3}} \right)}}{2} + \frac{\sqrt{3} b \sin{\left(\theta_{{2}{2}} \right)}}{4} + \frac{\sqrt{3} d_{2} \sin{\left(\theta_{{2}{2}} \right)}}{2}\\\frac{L \sin{\left(\theta_{{2}{2}} \right)} \cos{\left(\theta_{{2}{3}} \right)}}{2} + \frac{\sqrt{3} L \sin{\left(\theta_{{2}{3}} \right)}}{2} + \frac{b \sin{\left(\theta_{{2}{2}} \right)}}{4} + \frac{d_{2} \sin{\left(\theta_{{2}{2}} \right)}}{2}\\\left(L \cos{\left(\theta_{{2}{3}} \right)} + \frac{b}{2} + d_{2}\right) \cos{\left(\theta_{{2}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{1}{2}\\\frac{\sqrt{3}}{2}\\0\\\frac{\sqrt{3} L \sin{\left(\theta_{{2}{3}} \right)} \cos{\left(\theta_{{2}{2}} \right)}}{2}\\\frac{L \sin{\left(\theta_{{2}{3}} \right)} \cos{\left(\theta_{{2}{2}} \right)}}{2}\\- L \sin{\left(\theta_{{2}{2}} \right)} \sin{\left(\theta_{{2}{3}} \right)}\end{matrix}\right]\right]$

    Limb:  3

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\- \frac{1}{2}\\- \frac{\sqrt{3}}{2}\\0\end{matrix}\right], \  \left[\begin{matrix}- \frac{1}{2}\\- \frac{\sqrt{3}}{2}\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{3}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{3}{2}} \right)}}{2}\\\sin{\left(\theta_{{3}{2}} \right)}\\\frac{\sqrt{3} \left(b - 2 d_{3}\right) \sin{\left(\theta_{{3}{2}} \right)}}{4}\\\frac{\left(- b + 2 d_{3}\right) \sin{\left(\theta_{{3}{2}} \right)}}{4}\\\left(- \frac{b}{2} + d_{3}\right) \cos{\left(\theta_{{3}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{3}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{3}{2}} \right)}}{2}\\\sin{\left(\theta_{{3}{2}} \right)}\\- \frac{\sqrt{3} \left(b + 2 d_{3}\right) \sin{\left(\theta_{{3}{2}} \right)}}{4}\\\frac{\left(b + 2 d_{3}\right) \sin{\left(\theta_{{3}{2}} \right)}}{4}\\\left(\frac{b}{2} + d_{3}\right) \cos{\left(\theta_{{3}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{3}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{3}{2}} \right)}}{2}\\\sin{\left(\theta_{{3}{2}} \right)}\\- \frac{\sqrt{3} L \sin{\left(\theta_{{3}{2}} \right)} \cos{\left(\theta_{{3}{3}} \right)}}{2} - \frac{L \sin{\left(\theta_{{3}{3}} \right)}}{2} + \frac{\sqrt{3} b \sin{\left(\theta_{{3}{2}} \right)}}{4} - \frac{\sqrt{3} d_{3} \sin{\left(\theta_{{3}{2}} \right)}}{2}\\\frac{L \sin{\left(\theta_{{3}{2}} \right)} \cos{\left(\theta_{{3}{3}} \right)}}{2} - \frac{\sqrt{3} L \sin{\left(\theta_{{3}{3}} \right)}}{2} - \frac{b \sin{\left(\theta_{{3}{2}} \right)}}{4} + \frac{d_{3} \sin{\left(\theta_{{3}{2}} \right)}}{2}\\\left(L \cos{\left(\theta_{{3}{3}} \right)} - \frac{b}{2} + d_{3}\right) \cos{\left(\theta_{{3}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{3}{2}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{3}{2}} \right)}}{2}\\\sin{\left(\theta_{{3}{2}} \right)}\\- \frac{\sqrt{3} L \sin{\left(\theta_{{3}{2}} \right)} \cos{\left(\theta_{{3}{3}} \right)}}{2} - \frac{L \sin{\left(\theta_{{3}{3}} \right)}}{2} - \frac{\sqrt{3} b \sin{\left(\theta_{{3}{2}} \right)}}{4} - \frac{\sqrt{3} d_{3} \sin{\left(\theta_{{3}{2}} \right)}}{2}\\\frac{L \sin{\left(\theta_{{3}{2}} \right)} \cos{\left(\theta_{{3}{3}} \right)}}{2} - \frac{\sqrt{3} L \sin{\left(\theta_{{3}{3}} \right)}}{2} + \frac{b \sin{\left(\theta_{{3}{2}} \right)}}{4} + \frac{d_{3} \sin{\left(\theta_{{3}{2}} \right)}}{2}\\\left(L \cos{\left(\theta_{{3}{3}} \right)} + \frac{b}{2} + d_{3}\right) \cos{\left(\theta_{{3}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{1}{2}\\- \frac{\sqrt{3}}{2}\\0\\- \frac{\sqrt{3} L \sin{\left(\theta_{{3}{3}} \right)} \cos{\left(\theta_{{3}{2}} \right)}}{2}\\\frac{L \sin{\left(\theta_{{3}{3}} \right)} \cos{\left(\theta_{{3}{2}} \right)}}{2}\\- L \sin{\left(\theta_{{3}{2}} \right)} \sin{\left(\theta_{{3}{3}} \right)}\end{matrix}\right]\right]$

    Limb Wrench System
    ==================
    Limb:  1

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\- \tan{\left(\theta_{{1}{2}} \right)}\\1\end{matrix}\right]\right]$

    Limb:  2

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\\frac{\sqrt{3} \tan{\left(\theta_{{2}{2}} \right)}}{2}\\\frac{\tan{\left(\theta_{{2}{2}} \right)}}{2}\\1\end{matrix}\right]\right]$

    Limb:  3

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\- \frac{\sqrt{3} \tan{\left(\theta_{{3}{2}} \right)}}{2}\\\frac{\tan{\left(\theta_{{3}{2}} \right)}}{2}\\1\end{matrix}\right]\right]$

    Platform Wrench System
    ======================

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\- \tan{\left(\theta_{{1}{2}} \right)}\\1\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\\frac{\sqrt{3} \tan{\left(\theta_{{2}{2}} \right)}}{2}\\\frac{\tan{\left(\theta_{{2}{2}} \right)}}{2}\\1\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\- \frac{\sqrt{3} \tan{\left(\theta_{{3}{2}} \right)}}{2}\\\frac{\tan{\left(\theta_{{3}{2}} \right)}}{2}\\1\end{matrix}\right]\right]$

    Platform Twist System
    =====================

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right]\right]$

### 3.3. 4-PRRU Parallel Manipulators

The geometry of manipulator

**Examining the mobility with Grübler-Kutzbach mobility formula**

```python
n = 1 + 4 * 3 + 1
j = 4 * 4
Sigma_fi = 4 * (3 + 2)
print("Mobility of 4-PRRU using Grübler-Kutzbach mobility formulas: " \
      + str(Mobility_GK(n, j, Sigma_fi)))
```

    Mobility of 4-PRRU using Grübler-Kutzbach mobility formulas: 2

**The zeroth step:** A user defined for ${\bf{r}}_{ij}$, ${\bf{s}}_{ij}$ and $h_{ij}$ of 4-PRRU PM.

```python
def PM_4PRRU():
    # Number of limbs
    n_limbs = 4
    # Declaring sympy symbol for its kinematic parameter
    R, a, b, alpha = sym.symbols("R a b \\alpha")
    # Joint displacement and/or joint angular displacement
    act_spc = sym.symbols("d_{1:"+str(n_limbs+1)+"}")
    thetas = sym.symbols("\\theta_{{1:"+str(n_limbs+1)+"}{2:6}}")
    lambdas = sym.symbols("\\lambda_{1:"+str(n_limbs+1)+"}")
    lambdas_subs = dict(zip(lambdas, [0, sym.pi/2, sym.pi, 3*sym.pi/2]))
    # The unit vector of a unit screw pointing at for each limb
    _s = [mat.Matrix([0, 0, 1]),
          mat.Matrix([0, 1, 0]),
          mat.Matrix([0, 1, 0]),
          mat.Matrix([0, 1, 0]),
          mat.Matrix([0, 0, 1])]
    # pitch of the each joint for every limb
    _h = [sym.oo,] + 4 * [0,]
    # A fixed reference of frame O-XYZ
    G = mec.ReferenceFrame("G")
    # Within this loop, it is constructed all parameters required for
    # mobility calculation
    list_of_parameters = [] # Variable for containing all parameters
                            # required for mobility calculation
    kk = len(thetas)//n_limbs
    for ii in range(n_limbs):
        # Moving reference of frame
        H = G.orientnew("H"+str(ii+1), "Axis",
                        [lambdas[ii].subs(lambdas_subs), G.z])
        K = G.orientnew("K"+str(ii+1), "Axis", [alpha, G.x])
        N = G.orientnew("N"+str(ii+1), "Axis",
                        [thetas[ii*kk], K.y])
        Q = G.orientnew("Q"+str(ii+1), "Axis",
                        [thetas[ii*kk+1], K.y])
        # Position vector OA, AB, and BC in fixed reference of frame
        rOA = G.dcm(H) * mat.Matrix([R, 0, act_spc[ii]])
        rAB = G.dcm(H) * (G.dcm(K) * (G.dcm(N) * mat.Matrix([a, 0, 0])))
        rBC = G.dcm(H) * (G.dcm(N) * (G.dcm(N) * mat.Matrix([b, 0, 0])))
        # Contruct all parameters for each limb
        s_limb = []
        for jj, item in enumerate(_s):
            if jj in [1, 2, 3]:
                item = G.dcm(K) * item
            item = G.dcm(H) * item
            if jj in [0, 1]:
                _r = rOA
            elif jj == 2:
                _r = rOA + rAB
            elif jj in [3, 4]:
                _r = rOA + rAB + rBC
            s_limb.append([item, _r, _h[jj]])
        list_of_parameters.append(s_limb)
    return list_of_parameters

```

**The first to fourth step:**

```python
Mobility_PM_4PRRU = s4m.ManipulatorsMobility(PM_4PRRU(), "4-PRRU Parallel Manipulators")
```

**Displaying the result from the first to fourth step:**

```python
all_screw_system(Mobility_PM_4PRRU)
```

    Limb Twist System
    =================
    Limb:  1

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\alpha \right)}\\\sin{\left(\alpha \right)}\\- d_{1} \cos{\left(\alpha \right)}\\- R \sin{\left(\alpha \right)}\\R \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\alpha \right)}\\\sin{\left(\alpha \right)}\\\left(a \sin{\left(\theta_{{1}{2}} \right)} - d_{1}\right) \cos{\left(\alpha \right)}\\- \left(R + a \cos{\left(\theta_{{1}{2}} \right)}\right) \sin{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{1}{2}} \right)}\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\alpha \right)}\\\sin{\left(\alpha \right)}\\a \sin{\left(\theta_{{1}{2}} \right)} \cos{\left(\alpha \right)} + 2 b \sin{\left(\theta_{{1}{2}} \right)} \cos{\left(\theta_{{1}{2}} \right)} - d_{1} \cos{\left(\alpha \right)}\\\left(- R - a \cos{\left(\theta_{{1}{2}} \right)} - 2 b \cos^{2}{\left(\theta_{{1}{2}} \right)} + b\right) \sin{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{1}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{1}{2}} \right)} + b\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\2 \left(a \cos{\left(\alpha \right)} + b \cos{\left(\theta_{{1}{2}} \right)}\right) \sin{\left(\alpha \right)} \sin{\left(\theta_{{1}{2}} \right)}\\- R - a \cos{\left(\theta_{{1}{2}} \right)} - 2 b \cos^{2}{\left(\theta_{{1}{2}} \right)} + b\\0\end{matrix}\right]\right]$

    Limb:  2

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}- \cos{\left(\alpha \right)}\\0\\\sin{\left(\alpha \right)}\\R \sin{\left(\alpha \right)}\\- d_{2} \cos{\left(\alpha \right)}\\R \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}- \cos{\left(\alpha \right)}\\0\\\sin{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{2}{2}} \right)}\right) \sin{\left(\alpha \right)}\\\left(a \sin{\left(\theta_{{2}{2}} \right)} - d_{2}\right) \cos{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{2}{2}} \right)}\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}- \cos{\left(\alpha \right)}\\0\\\sin{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{2}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{2}{2}} \right)} + b\right) \sin{\left(\alpha \right)}\\a \sin{\left(\theta_{{2}{2}} \right)} \cos{\left(\alpha \right)} + 2 b \sin{\left(\theta_{{2}{2}} \right)} \cos{\left(\theta_{{2}{2}} \right)} - d_{2} \cos{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{2}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{2}{2}} \right)} + b\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\R + a \cos{\left(\theta_{{2}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{2}{2}} \right)} + b\\2 \left(a \cos{\left(\alpha \right)} + b \cos{\left(\theta_{{2}{2}} \right)}\right) \sin{\left(\alpha \right)} \sin{\left(\theta_{{2}{2}} \right)}\\0\end{matrix}\right]\right]$

    Limb:  3

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}0\\- \cos{\left(\alpha \right)}\\\sin{\left(\alpha \right)}\\d_{3} \cos{\left(\alpha \right)}\\R \sin{\left(\alpha \right)}\\R \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\- \cos{\left(\alpha \right)}\\\sin{\left(\alpha \right)}\\\left(- a \sin{\left(\theta_{{3}{2}} \right)} + d_{3}\right) \cos{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{3}{2}} \right)}\right) \sin{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{3}{2}} \right)}\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\- \cos{\left(\alpha \right)}\\\sin{\left(\alpha \right)}\\- a \sin{\left(\theta_{{3}{2}} \right)} \cos{\left(\alpha \right)} - 2 b \sin{\left(\theta_{{3}{2}} \right)} \cos{\left(\theta_{{3}{2}} \right)} + d_{3} \cos{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{3}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{3}{2}} \right)} + b\right) \sin{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{3}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{3}{2}} \right)} + b\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\- 2 \left(a \cos{\left(\alpha \right)} + b \cos{\left(\theta_{{3}{2}} \right)}\right) \sin{\left(\alpha \right)} \sin{\left(\theta_{{3}{2}} \right)}\\R + a \cos{\left(\theta_{{3}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{3}{2}} \right)} + b\\0\end{matrix}\right]\right]$

    Limb:  4

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}\cos{\left(\alpha \right)}\\0\\\sin{\left(\alpha \right)}\\- R \sin{\left(\alpha \right)}\\d_{4} \cos{\left(\alpha \right)}\\R \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}\cos{\left(\alpha \right)}\\0\\\sin{\left(\alpha \right)}\\- \left(R + a \cos{\left(\theta_{{4}{2}} \right)}\right) \sin{\left(\alpha \right)}\\\left(- a \sin{\left(\theta_{{4}{2}} \right)} + d_{4}\right) \cos{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{4}{2}} \right)}\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}\cos{\left(\alpha \right)}\\0\\\sin{\left(\alpha \right)}\\\left(- R - a \cos{\left(\theta_{{4}{2}} \right)} - 2 b \cos^{2}{\left(\theta_{{4}{2}} \right)} + b\right) \sin{\left(\alpha \right)}\\- a \sin{\left(\theta_{{4}{2}} \right)} \cos{\left(\alpha \right)} - 2 b \sin{\left(\theta_{{4}{2}} \right)} \cos{\left(\theta_{{4}{2}} \right)} + d_{4} \cos{\left(\alpha \right)}\\\left(R + a \cos{\left(\theta_{{4}{2}} \right)} - 2 b \sin^{2}{\left(\theta_{{4}{2}} \right)} + b\right) \cos{\left(\alpha \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\- R - a \cos{\left(\theta_{{4}{2}} \right)} - 2 b \cos^{2}{\left(\theta_{{4}{2}} \right)} + b\\- 2 \left(a \cos{\left(\alpha \right)} + b \cos{\left(\theta_{{4}{2}} \right)}\right) \sin{\left(\alpha \right)} \sin{\left(\theta_{{4}{2}} \right)}\\0\end{matrix}\right]\right]$

    Limb Wrench System
    ==================
    Limb:  1

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right]\right]$

    Limb:  2

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right]\right]$

    Limb:  3

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right]\right]$

    Limb:  4

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right]\right]$

    Platform Wrench System
    ======================

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right]\right]$

    Platform Twist System
    =====================

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\0\\0\\0\end{matrix}\right]\right]$

### 3.4. 6-UPS Parallel Manipulators

The geometry of manipulator

**Examining the mobility with Grübler-Kutzbach mobility formula**

```python
n = 1 + 6 * 2 + 1
j = 6 * 3
Sigma_fi = 6 * (2 + 1 + 3)
print("Mobility of 6-UPS using Grübler-Kutzbach mobility formulas: " \
      + str(Mobility_GK(n, j, Sigma_fi)))
```

    Mobility of 6-UPS using Grübler-Kutzbach mobility formulas: 6

**The zeroth step:** A user defined for ${\bf{r}}_{ij}$, ${\bf{s}}_{ij}$ and $h_{ij}$ of 6-UPS PM.

```python
def PM_6UPS():
    # Number of limbs
    n_limbs = 6
    # Declaring sympy symbol for its kinematic parameter
    R, r = sym.symbols("R r")
    # Joint displacement and/or joint angular displacement
    act_spc = sym.symbols("d_{1:"+str(n_limbs+1)+"}")
    thetas = sym.symbols("\\theta_{{1:"+str(n_limbs+1)+"}{1:6}}")
    lambdas = sym.symbols("\\lambda_{1:"+str(n_limbs+1)+"}")
    lambdas_subs = dict(zip(lambdas, [ii*sym.pi/3 for ii in range(n_limbs)]))
    # The unit vector of a unit screw pointing at for each limb
    _s = [mat.Matrix([1, 0, 0]),
          mat.Matrix([0, 1, 0]),
          mat.Matrix([0, 0, 1]),
          mat.Matrix([1, 0, 0]),
          mat.Matrix([0, 1, 0]),
          mat.Matrix([0, 0, 1])]
    # pitch of the each joint for every limb
    _h = 2 * [0,] + [sym.oo,] + 3 * [0,]
    # A fixed reference of frame O-XYZ
    G = mec.ReferenceFrame("G")
    # Within this loop, it is constructed all parameters required for
    # mobility calculation
    list_of_parameters = [] # Variable for containing all parameters
                            # required for mobility calculation
    kk = len(thetas)//n_limbs
    for ii in range(n_limbs):
        # Moving reference of frame
        H = G.orientnew("H"+str(ii+1), "Axis",
                        [lambdas[ii].subs(lambdas_subs), G.z])
        K = G.orientnew("K"+str(ii+1), "Axis", [thetas[ii*kk], G.x])
        N = K.orientnew("N"+str(ii+1), "Axis", [thetas[ii*kk+1], K.y])
        # Position vector AB in fixed reference of frame
        rAB = G.dcm(H) * (G.dcm(K) * mat.Matrix([0, 0, act_spc[ii]]))
        # Contruct all parameters for each limb
        s_limb = []
        for jj, item in enumerate(_s):
            if jj == 1:
                item = G.dcm(K) * item
            elif jj in [2, 3, 4, 5]:
                item = G.dcm(N) * item
            item = G.dcm(H) * item
            _r = G.dcm(H) * mat.Matrix([R, 0, 0])
            if jj in [3, 4, 5]:
                _r += rAB
            s_limb.append([item, _r, _h[jj]])
        list_of_parameters.append(s_limb)
    return list_of_parameters

```

**The first to fourth step:**

```python
Mobility_PM_6UPS = s4m.ManipulatorsMobility(PM_6UPS(), "6-UPS Parallel Manipulators")
```

```python
Mobility_PM_6UPS.Mobility
```

    {'Number': 6,
     'Type': '3T3R',
     'Direction': ['Tx', 'Ty', 'Tz', 'Rx', 'Ry', 'Rz']}

**Displaying the result from the first to fourth step:**

```python
all_screw_system(Mobility_PM_6UPS)
```

    Limb Twist System
    =================
    Limb:  1

$\displaystyle \left[ \left[\begin{matrix}1\\0\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\theta_{{1}{1}} \right)}\\\sin{\left(\theta_{{1}{1}} \right)}\\0\\- R \sin{\left(\theta_{{1}{1}} \right)}\\R \cos{\left(\theta_{{1}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\\sin{\left(\theta_{{1}{2}} \right)}\\- \sin{\left(\theta_{{1}{1}} \right)} \cos{\left(\theta_{{1}{2}} \right)}\\\cos{\left(\theta_{{1}{1}} \right)} \cos{\left(\theta_{{1}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\cos{\left(\theta_{{1}{2}} \right)}\\\sin{\left(\theta_{{1}{1}} \right)} \sin{\left(\theta_{{1}{2}} \right)}\\- \sin{\left(\theta_{{1}{2}} \right)} \cos{\left(\theta_{{1}{1}} \right)}\\0\\\left(R \sin{\left(\theta_{{1}{2}} \right)} + d_{1} \cos{\left(\theta_{{1}{2}} \right)}\right) \cos{\left(\theta_{{1}{1}} \right)}\\\left(R \sin{\left(\theta_{{1}{2}} \right)} + d_{1} \cos{\left(\theta_{{1}{2}} \right)}\right) \sin{\left(\theta_{{1}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\\cos{\left(\theta_{{1}{1}} \right)}\\\sin{\left(\theta_{{1}{1}} \right)}\\- d_{1}\\- R \sin{\left(\theta_{{1}{1}} \right)}\\R \cos{\left(\theta_{{1}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\sin{\left(\theta_{{1}{2}} \right)}\\- \sin{\left(\theta_{{1}{1}} \right)} \cos{\left(\theta_{{1}{2}} \right)}\\\cos{\left(\theta_{{1}{1}} \right)} \cos{\left(\theta_{{1}{2}} \right)}\\0\\\left(- R \cos{\left(\theta_{{1}{2}} \right)} + d_{1} \sin{\left(\theta_{{1}{2}} \right)}\right) \cos{\left(\theta_{{1}{1}} \right)}\\\left(- R \cos{\left(\theta_{{1}{2}} \right)} + d_{1} \sin{\left(\theta_{{1}{2}} \right)}\right) \sin{\left(\theta_{{1}{1}} \right)}\end{matrix}\right]\right]$

    Limb:  2

$\displaystyle \left[ \left[\begin{matrix}\frac{1}{2}\\\frac{\sqrt{3}}{2}\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{2}{1}} \right)}}{2}\\\frac{\cos{\left(\theta_{{2}{1}} \right)}}{2}\\\sin{\left(\theta_{{2}{1}} \right)}\\\frac{\sqrt{3} R \sin{\left(\theta_{{2}{1}} \right)}}{2}\\- \frac{R \sin{\left(\theta_{{2}{1}} \right)}}{2}\\R \cos{\left(\theta_{{2}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\\frac{\sqrt{3} \sin{\left(\theta_{{2}{1}} \right)} \cos{\left(\theta_{{2}{2}} \right)}}{2} + \frac{\sin{\left(\theta_{{2}{2}} \right)}}{2}\\- \frac{\sin{\left(\theta_{{2}{1}} \right)} \cos{\left(\theta_{{2}{2}} \right)}}{2} + \frac{\sqrt{3} \sin{\left(\theta_{{2}{2}} \right)}}{2}\\\cos{\left(\theta_{{2}{1}} \right)} \cos{\left(\theta_{{2}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \sin{\left(\theta_{{2}{1}} \right)} \sin{\left(\theta_{{2}{2}} \right)}}{2} + \frac{\cos{\left(\theta_{{2}{2}} \right)}}{2}\\\frac{\sin{\left(\theta_{{2}{1}} \right)} \sin{\left(\theta_{{2}{2}} \right)}}{2} + \frac{\sqrt{3} \cos{\left(\theta_{{2}{2}} \right)}}{2}\\- \sin{\left(\theta_{{2}{2}} \right)} \cos{\left(\theta_{{2}{1}} \right)}\\- \frac{\sqrt{3} \left(R \sin{\left(\theta_{{2}{2}} \right)} + d_{2} \cos{\left(\theta_{{2}{2}} \right)}\right) \cos{\left(\theta_{{2}{1}} \right)}}{2}\\\frac{\left(R \sin{\left(\theta_{{2}{2}} \right)} + d_{2} \cos{\left(\theta_{{2}{2}} \right)}\right) \cos{\left(\theta_{{2}{1}} \right)}}{2}\\\left(R \sin{\left(\theta_{{2}{2}} \right)} + d_{2} \cos{\left(\theta_{{2}{2}} \right)}\right) \sin{\left(\theta_{{2}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{2}{1}} \right)}}{2}\\\frac{\cos{\left(\theta_{{2}{1}} \right)}}{2}\\\sin{\left(\theta_{{2}{1}} \right)}\\\frac{\sqrt{3} R \sin{\left(\theta_{{2}{1}} \right)}}{2} - \frac{d_{2}}{2}\\- \frac{R \sin{\left(\theta_{{2}{1}} \right)}}{2} - \frac{\sqrt{3} d_{2}}{2}\\R \cos{\left(\theta_{{2}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \sin{\left(\theta_{{2}{1}} \right)} \cos{\left(\theta_{{2}{2}} \right)}}{2} + \frac{\sin{\left(\theta_{{2}{2}} \right)}}{2}\\- \frac{\sin{\left(\theta_{{2}{1}} \right)} \cos{\left(\theta_{{2}{2}} \right)}}{2} + \frac{\sqrt{3} \sin{\left(\theta_{{2}{2}} \right)}}{2}\\\cos{\left(\theta_{{2}{1}} \right)} \cos{\left(\theta_{{2}{2}} \right)}\\\frac{\sqrt{3} \left(R \cos{\left(\theta_{{2}{2}} \right)} - d_{2} \sin{\left(\theta_{{2}{2}} \right)}\right) \cos{\left(\theta_{{2}{1}} \right)}}{2}\\\frac{\left(- R \cos{\left(\theta_{{2}{2}} \right)} + d_{2} \sin{\left(\theta_{{2}{2}} \right)}\right) \cos{\left(\theta_{{2}{1}} \right)}}{2}\\\left(- R \cos{\left(\theta_{{2}{2}} \right)} + d_{2} \sin{\left(\theta_{{2}{2}} \right)}\right) \sin{\left(\theta_{{2}{1}} \right)}\end{matrix}\right]\right]$

    Limb:  3

$\displaystyle \left[ \left[\begin{matrix}- \frac{1}{2}\\\frac{\sqrt{3}}{2}\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{3}{1}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{3}{1}} \right)}}{2}\\\sin{\left(\theta_{{3}{1}} \right)}\\\frac{\sqrt{3} R \sin{\left(\theta_{{3}{1}} \right)}}{2}\\\frac{R \sin{\left(\theta_{{3}{1}} \right)}}{2}\\R \cos{\left(\theta_{{3}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\\frac{\sqrt{3} \sin{\left(\theta_{{3}{1}} \right)} \cos{\left(\theta_{{3}{2}} \right)}}{2} - \frac{\sin{\left(\theta_{{3}{2}} \right)}}{2}\\\frac{\sin{\left(\theta_{{3}{1}} \right)} \cos{\left(\theta_{{3}{2}} \right)}}{2} + \frac{\sqrt{3} \sin{\left(\theta_{{3}{2}} \right)}}{2}\\\cos{\left(\theta_{{3}{1}} \right)} \cos{\left(\theta_{{3}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \sin{\left(\theta_{{3}{1}} \right)} \sin{\left(\theta_{{3}{2}} \right)}}{2} - \frac{\cos{\left(\theta_{{3}{2}} \right)}}{2}\\- \frac{\sin{\left(\theta_{{3}{1}} \right)} \sin{\left(\theta_{{3}{2}} \right)}}{2} + \frac{\sqrt{3} \cos{\left(\theta_{{3}{2}} \right)}}{2}\\- \sin{\left(\theta_{{3}{2}} \right)} \cos{\left(\theta_{{3}{1}} \right)}\\- \frac{\sqrt{3} \left(R \sin{\left(\theta_{{3}{2}} \right)} + d_{3} \cos{\left(\theta_{{3}{2}} \right)}\right) \cos{\left(\theta_{{3}{1}} \right)}}{2}\\- \frac{\left(R \sin{\left(\theta_{{3}{2}} \right)} + d_{3} \cos{\left(\theta_{{3}{2}} \right)}\right) \cos{\left(\theta_{{3}{1}} \right)}}{2}\\\left(R \sin{\left(\theta_{{3}{2}} \right)} + d_{3} \cos{\left(\theta_{{3}{2}} \right)}\right) \sin{\left(\theta_{{3}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \cos{\left(\theta_{{3}{1}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{3}{1}} \right)}}{2}\\\sin{\left(\theta_{{3}{1}} \right)}\\\frac{\sqrt{3} R \sin{\left(\theta_{{3}{1}} \right)}}{2} + \frac{d_{3}}{2}\\\frac{R \sin{\left(\theta_{{3}{1}} \right)}}{2} - \frac{\sqrt{3} d_{3}}{2}\\R \cos{\left(\theta_{{3}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \sin{\left(\theta_{{3}{1}} \right)} \cos{\left(\theta_{{3}{2}} \right)}}{2} - \frac{\sin{\left(\theta_{{3}{2}} \right)}}{2}\\\frac{\sin{\left(\theta_{{3}{1}} \right)} \cos{\left(\theta_{{3}{2}} \right)}}{2} + \frac{\sqrt{3} \sin{\left(\theta_{{3}{2}} \right)}}{2}\\\cos{\left(\theta_{{3}{1}} \right)} \cos{\left(\theta_{{3}{2}} \right)}\\\frac{\sqrt{3} \left(R \cos{\left(\theta_{{3}{2}} \right)} - d_{3} \sin{\left(\theta_{{3}{2}} \right)}\right) \cos{\left(\theta_{{3}{1}} \right)}}{2}\\\frac{\left(R \cos{\left(\theta_{{3}{2}} \right)} - d_{3} \sin{\left(\theta_{{3}{2}} \right)}\right) \cos{\left(\theta_{{3}{1}} \right)}}{2}\\\left(- R \cos{\left(\theta_{{3}{2}} \right)} + d_{3} \sin{\left(\theta_{{3}{2}} \right)}\right) \sin{\left(\theta_{{3}{1}} \right)}\end{matrix}\right]\right]$

    Limb:  4

$\displaystyle \left[ \left[\begin{matrix}-1\\0\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\- \cos{\left(\theta_{{4}{1}} \right)}\\\sin{\left(\theta_{{4}{1}} \right)}\\0\\R \sin{\left(\theta_{{4}{1}} \right)}\\R \cos{\left(\theta_{{4}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\- \sin{\left(\theta_{{4}{2}} \right)}\\\sin{\left(\theta_{{4}{1}} \right)} \cos{\left(\theta_{{4}{2}} \right)}\\\cos{\left(\theta_{{4}{1}} \right)} \cos{\left(\theta_{{4}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \cos{\left(\theta_{{4}{2}} \right)}\\- \sin{\left(\theta_{{4}{1}} \right)} \sin{\left(\theta_{{4}{2}} \right)}\\- \sin{\left(\theta_{{4}{2}} \right)} \cos{\left(\theta_{{4}{1}} \right)}\\0\\- \left(R \sin{\left(\theta_{{4}{2}} \right)} + d_{4} \cos{\left(\theta_{{4}{2}} \right)}\right) \cos{\left(\theta_{{4}{1}} \right)}\\\left(R \sin{\left(\theta_{{4}{2}} \right)} + d_{4} \cos{\left(\theta_{{4}{2}} \right)}\right) \sin{\left(\theta_{{4}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\- \cos{\left(\theta_{{4}{1}} \right)}\\\sin{\left(\theta_{{4}{1}} \right)}\\d_{4}\\R \sin{\left(\theta_{{4}{1}} \right)}\\R \cos{\left(\theta_{{4}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \sin{\left(\theta_{{4}{2}} \right)}\\\sin{\left(\theta_{{4}{1}} \right)} \cos{\left(\theta_{{4}{2}} \right)}\\\cos{\left(\theta_{{4}{1}} \right)} \cos{\left(\theta_{{4}{2}} \right)}\\0\\\left(R \cos{\left(\theta_{{4}{2}} \right)} - d_{4} \sin{\left(\theta_{{4}{2}} \right)}\right) \cos{\left(\theta_{{4}{1}} \right)}\\\left(- R \cos{\left(\theta_{{4}{2}} \right)} + d_{4} \sin{\left(\theta_{{4}{2}} \right)}\right) \sin{\left(\theta_{{4}{1}} \right)}\end{matrix}\right]\right]$

    Limb:  5

$\displaystyle \left[ \left[\begin{matrix}- \frac{1}{2}\\- \frac{\sqrt{3}}{2}\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{5}{1}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{5}{1}} \right)}}{2}\\\sin{\left(\theta_{{5}{1}} \right)}\\- \frac{\sqrt{3} R \sin{\left(\theta_{{5}{1}} \right)}}{2}\\\frac{R \sin{\left(\theta_{{5}{1}} \right)}}{2}\\R \cos{\left(\theta_{{5}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\- \frac{\sqrt{3} \sin{\left(\theta_{{5}{1}} \right)} \cos{\left(\theta_{{5}{2}} \right)}}{2} - \frac{\sin{\left(\theta_{{5}{2}} \right)}}{2}\\\frac{\sin{\left(\theta_{{5}{1}} \right)} \cos{\left(\theta_{{5}{2}} \right)}}{2} - \frac{\sqrt{3} \sin{\left(\theta_{{5}{2}} \right)}}{2}\\\cos{\left(\theta_{{5}{1}} \right)} \cos{\left(\theta_{{5}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \sin{\left(\theta_{{5}{1}} \right)} \sin{\left(\theta_{{5}{2}} \right)}}{2} - \frac{\cos{\left(\theta_{{5}{2}} \right)}}{2}\\- \frac{\sin{\left(\theta_{{5}{1}} \right)} \sin{\left(\theta_{{5}{2}} \right)}}{2} - \frac{\sqrt{3} \cos{\left(\theta_{{5}{2}} \right)}}{2}\\- \sin{\left(\theta_{{5}{2}} \right)} \cos{\left(\theta_{{5}{1}} \right)}\\\frac{\sqrt{3} \left(R \sin{\left(\theta_{{5}{2}} \right)} + d_{5} \cos{\left(\theta_{{5}{2}} \right)}\right) \cos{\left(\theta_{{5}{1}} \right)}}{2}\\- \frac{\left(R \sin{\left(\theta_{{5}{2}} \right)} + d_{5} \cos{\left(\theta_{{5}{2}} \right)}\right) \cos{\left(\theta_{{5}{1}} \right)}}{2}\\\left(R \sin{\left(\theta_{{5}{2}} \right)} + d_{5} \cos{\left(\theta_{{5}{2}} \right)}\right) \sin{\left(\theta_{{5}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{5}{1}} \right)}}{2}\\- \frac{\cos{\left(\theta_{{5}{1}} \right)}}{2}\\\sin{\left(\theta_{{5}{1}} \right)}\\- \frac{\sqrt{3} R \sin{\left(\theta_{{5}{1}} \right)}}{2} + \frac{d_{5}}{2}\\\frac{R \sin{\left(\theta_{{5}{1}} \right)}}{2} + \frac{\sqrt{3} d_{5}}{2}\\R \cos{\left(\theta_{{5}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \sin{\left(\theta_{{5}{1}} \right)} \cos{\left(\theta_{{5}{2}} \right)}}{2} - \frac{\sin{\left(\theta_{{5}{2}} \right)}}{2}\\\frac{\sin{\left(\theta_{{5}{1}} \right)} \cos{\left(\theta_{{5}{2}} \right)}}{2} - \frac{\sqrt{3} \sin{\left(\theta_{{5}{2}} \right)}}{2}\\\cos{\left(\theta_{{5}{1}} \right)} \cos{\left(\theta_{{5}{2}} \right)}\\\frac{\sqrt{3} \left(- R \cos{\left(\theta_{{5}{2}} \right)} + d_{5} \sin{\left(\theta_{{5}{2}} \right)}\right) \cos{\left(\theta_{{5}{1}} \right)}}{2}\\\frac{\left(R \cos{\left(\theta_{{5}{2}} \right)} - d_{5} \sin{\left(\theta_{{5}{2}} \right)}\right) \cos{\left(\theta_{{5}{1}} \right)}}{2}\\\left(- R \cos{\left(\theta_{{5}{2}} \right)} + d_{5} \sin{\left(\theta_{{5}{2}} \right)}\right) \sin{\left(\theta_{{5}{1}} \right)}\end{matrix}\right]\right]$

    Limb:  6

$\displaystyle \left[ \left[\begin{matrix}\frac{1}{2}\\- \frac{\sqrt{3}}{2}\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{6}{1}} \right)}}{2}\\\frac{\cos{\left(\theta_{{6}{1}} \right)}}{2}\\\sin{\left(\theta_{{6}{1}} \right)}\\- \frac{\sqrt{3} R \sin{\left(\theta_{{6}{1}} \right)}}{2}\\- \frac{R \sin{\left(\theta_{{6}{1}} \right)}}{2}\\R \cos{\left(\theta_{{6}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\- \frac{\sqrt{3} \sin{\left(\theta_{{6}{1}} \right)} \cos{\left(\theta_{{6}{2}} \right)}}{2} + \frac{\sin{\left(\theta_{{6}{2}} \right)}}{2}\\- \frac{\sin{\left(\theta_{{6}{1}} \right)} \cos{\left(\theta_{{6}{2}} \right)}}{2} - \frac{\sqrt{3} \sin{\left(\theta_{{6}{2}} \right)}}{2}\\\cos{\left(\theta_{{6}{1}} \right)} \cos{\left(\theta_{{6}{2}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \sin{\left(\theta_{{6}{1}} \right)} \sin{\left(\theta_{{6}{2}} \right)}}{2} + \frac{\cos{\left(\theta_{{6}{2}} \right)}}{2}\\\frac{\sin{\left(\theta_{{6}{1}} \right)} \sin{\left(\theta_{{6}{2}} \right)}}{2} - \frac{\sqrt{3} \cos{\left(\theta_{{6}{2}} \right)}}{2}\\- \sin{\left(\theta_{{6}{2}} \right)} \cos{\left(\theta_{{6}{1}} \right)}\\\frac{\sqrt{3} \left(R \sin{\left(\theta_{{6}{2}} \right)} + d_{6} \cos{\left(\theta_{{6}{2}} \right)}\right) \cos{\left(\theta_{{6}{1}} \right)}}{2}\\\frac{\left(R \sin{\left(\theta_{{6}{2}} \right)} + d_{6} \cos{\left(\theta_{{6}{2}} \right)}\right) \cos{\left(\theta_{{6}{1}} \right)}}{2}\\\left(R \sin{\left(\theta_{{6}{2}} \right)} + d_{6} \cos{\left(\theta_{{6}{2}} \right)}\right) \sin{\left(\theta_{{6}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}\frac{\sqrt{3} \cos{\left(\theta_{{6}{1}} \right)}}{2}\\\frac{\cos{\left(\theta_{{6}{1}} \right)}}{2}\\\sin{\left(\theta_{{6}{1}} \right)}\\- \frac{\sqrt{3} R \sin{\left(\theta_{{6}{1}} \right)}}{2} - \frac{d_{6}}{2}\\- \frac{R \sin{\left(\theta_{{6}{1}} \right)}}{2} + \frac{\sqrt{3} d_{6}}{2}\\R \cos{\left(\theta_{{6}{1}} \right)}\end{matrix}\right], \  \left[\begin{matrix}- \frac{\sqrt{3} \sin{\left(\theta_{{6}{1}} \right)} \cos{\left(\theta_{{6}{2}} \right)}}{2} + \frac{\sin{\left(\theta_{{6}{2}} \right)}}{2}\\- \frac{\sin{\left(\theta_{{6}{1}} \right)} \cos{\left(\theta_{{6}{2}} \right)}}{2} - \frac{\sqrt{3} \sin{\left(\theta_{{6}{2}} \right)}}{2}\\\cos{\left(\theta_{{6}{1}} \right)} \cos{\left(\theta_{{6}{2}} \right)}\\\frac{\sqrt{3} \left(- R \cos{\left(\theta_{{6}{2}} \right)} + d_{6} \sin{\left(\theta_{{6}{2}} \right)}\right) \cos{\left(\theta_{{6}{1}} \right)}}{2}\\\frac{\left(- R \cos{\left(\theta_{{6}{2}} \right)} + d_{6} \sin{\left(\theta_{{6}{2}} \right)}\right) \cos{\left(\theta_{{6}{1}} \right)}}{2}\\\left(- R \cos{\left(\theta_{{6}{2}} \right)} + d_{6} \sin{\left(\theta_{{6}{2}} \right)}\right) \sin{\left(\theta_{{6}{1}} \right)}\end{matrix}\right]\right]$

    Limb Wrench System
    ==================
    Limb:  1

$\displaystyle \left[ \right]$

    Limb:  2

$\displaystyle \left[ \right]$

    Limb:  3

$\displaystyle \left[ \right]$

    Limb:  4

$\displaystyle \left[ \right]$

    Limb:  5

$\displaystyle \left[ \right]$

    Limb:  6

$\displaystyle \left[ \right]$

    Platform Wrench System
    ======================

$\displaystyle \left[ \right]$

    Platform Twist System
    =====================

$\displaystyle \left[ \left[\begin{matrix}0\\0\\0\\1\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\1\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\0\\0\\0\\1\end{matrix}\right], \  \left[\begin{matrix}1\\0\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\1\\0\\0\\0\\0\end{matrix}\right], \  \left[\begin{matrix}0\\0\\1\\0\\0\\0\end{matrix}\right]\right]$

## 4. Computation Time for Each PM

**The 3-PRRR Parallel Manipulator**

```python
%%timeit -n 1 -r 10
s4m.ManipulatorsMobility(PM_3PRRR())
```

    1.16 s ± 45.1 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

**The 3-PR(Pa)R Parallel Manipulator**

```python
%%timeit -n 1 -r 10
s4m.ManipulatorsMobility(PM_3PRPaR())
```

    5.24 s ± 147 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

**The 4-PRRU Parallel Manipulator**

```python
%%timeit -n 1 -r 10
s4m.ManipulatorsMobility(PM_4PRRU())
```

    9.01 s ± 329 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

**The 6-UPS Parallel Manipulator**

```python
%%timeit -n 1 -r 10
s4m.ManipulatorsMobility(PM_6UPS())
```

    25.9 s ± 418 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

---

Screw Theory for Mobility Determination of PMs.ipynb


# 1. 有向图与无向图

[概率图模型](https://zhuanlan.zhihu.com/p/54101808)

概率图模型三大理论部分：

1. 表示
    1. 有向图（离散）：贝叶斯网络

    2. 高斯图（连续）：高斯贝叶斯和高斯Markov网络

    3. 无向图（离散）：Markov网络

2. 推断

    1. 精确推断
        - 变量消去法

        - 信念传播法

    2. 近似推断
        - 确定性近似（VI）

        - 随机近似（MCMC）

3. 学习
    1. 参数学习
        - 完备数据

        - 隐变量：EM法

    2. 结构学习

## 1.1 有向图——贝叶斯网络

已知联合分布中，各个随机变量之间的依赖关系，那么可以通过拓扑排序（根据依赖关系）可以获得一个有向图。而如果已知一个图，也可以直接得到联合概率分布的因子分解：

$$p\left(x_{1}, x_{2}, \cdots, x_{p}\right)=\prod_{i=1}^{p} p\left(x_{i} \mid x_{\text {parent }(i)}\right)$$

图中条件独立性是如何体现的呢？在局部任何三个结点，可以构成三种结构：

## 1.2 无向图——Markov网络（Markov Random Field）

在Markov网络中，也存在D划分的概念：
- 全局马尔可夫性
- 局部马尔可夫性
- 成对马尔科夫性

联合概率可以分解为最大团上势函数的乘积形式：

$$
\begin{align}
p(x) &= \frac{1}{Z}\prod_{i=1}^{K}\phi(x_{ci}) \\
Z &= \sum_{x\in \mathcal{X}} \prod_{i=1}^{K}\phi(x_{ci})
\end{align}
$$

其中$\phi(x_{ci})$叫做势函数，它必须是一个正值，可以记为：

$$
\phi(x_{ci}) = \exp(-E(x_{ci}))
$$

这个分布叫作Gibbs分布（玻尔兹曼分布），这个分解和条件独立性等价（Hammesley-Clifford定理），这个分布的形式也和指数族分布形式上相同，于是满足最大熵原理。

## 1.3 推断

通过概率图模型，我们得到了唯一的联合概率分布，通过联合概率分布我们一般可以计算变量的边缘分布和条件分布，其中计算条件分布的过程即对应推断任务。

通常推断可以分为：

1. 精确推断
    - Variable Elimination（VE）
    - Belief Propagation（BP，Sum-Product Algorithm）
    - Junction Tree，上面两种在树结构上应用，Junction Tree在图结构上应用

2. 近似推断
    - Mente Carlo Interference，如 Importance Sampling，MCMC
    - Variational Inference

## 1.4 推断——变量消除（VE）

变量消除的方法是在求解概率分布的时候，将相关的条件概率先行求和或积分，从而一步步地消除变量，例如在马尔科夫链中：

变量消除的缺点很明显：

1. 计算步骤无法存储

2. 消除的最优次序是一个NP-hard问题

## 1.5 推断——信念传播

## 1.6 推断——Max-Product算法

## 1.7 推断——MCMC

在现实的许多情况下我们需要计算某些变量的和或积分时，由于其数目较多，直接计算行不通。这是可以引入蒙特卡洛采用的方法，将求和或积分运算转化为求某一特定分布下的期望，进而通过平均值近似期望得到所求结果。

此外，如果样本服从i.i.d分布，则根据大数定律，其估值均值必将收敛至期望值。这也保证了蒙特卡洛采样的合理性和估计的无偏性。

## 1.8 推断——变分推断

# 2. 生成 VS. 判别

1. 判别模型

    判别方法由数据直接学习决策函数$f(x)$ 或者条件概率分布 $P(y \mid x)$ 作为预测的模型，即判别模型。

    判别方法关心的是对给定输入 $x$ ，应该预测什么样的输出 $y$ 。

2. 生成模型

    生成方法由数据学习输入和输出的联合概率分布 $P(x,y)$，然后求出后验概率分布 $P(y\mid x)$ 作为预测的模型，即生成模型。以Naive Bayes为例，我们要求的目标可以通过：

    $$
    P(x, y) = P(x \mid y) P(y)
    $$

    求出输入输出的联合概率分布，然后通过贝叶斯公式：

    $$
    P(y \mid x) = \frac{P(x \mid y)P(y)}{P(x)}
    $$

    求出后验概率分布。

3. 判别模型优缺点

    - 优点：

        - 仅需要有限的样本。节省计算资源，需要的样本数量也少于生成模型。

        - 由于直接学习$P(y\mid x)$，而不需要求解类别条件概率，所以允许我们对输入进行抽象（比如降维、构造），从而能够简化学习问题。

    - 缺点：

        - 不能反映训练数据本身的特性。能力有限，可以告诉你的是1还是2，但没有办法把整个场景描述出来。

        - 黑盒操作：变量间的关系不清楚，不可见。

4. 生成模型优缺点

    - 优点：

        - 生成模型给出的是联合分布 $P(x,y)$，不仅能够由联合分布计算后验分布 $P(y\mid x)$，还可以给出其它信息，比如可以使用 $P(x) = \sum_{i=1}^{k}P(x\mid y_{i})P(y_{i})$ 来计算边缘分布 $P(x)$。如果一个输入样本的边缘分布 $P(x)$ 很小的话，那么可以认为学习出的这个模型可能不太适合对这个样本进行分类，分类效果可能不好，这也是所谓的异常值检测（outlier detection）。

        - 生成模型收敛速度比较快，即当样本数量较多时，生成模型能更快地收敛于真实模型；

        - 生成模型能够应付存在隐变量的情况，比如GMM就是含有隐变量的生成方法；

     - 缺点

         - 联合分布能够提供更多的信息，但也需要更多的样本和更多计算，尤其是为了更准确估计类别条件分布，需要增加样本的数目，而且类别条件概率的许多信息是我们做分类用不到，因而如果我们只需要做分类任务，就浪费了计算资源；

         - 另外，实践中多数情况下判别模型效果更好。

5. 典型的生成模型：

    - Naive Bayes
    - HMM

6. 典型的判别模型

    - k-nn
    - perceptron
    - decision tree
    - Logistic Regression
    - Maximum Entropy Model
    - SVM
    - CRF

# 3. HMM—>MEMM—>CRF

## 3.1 HMM

对联合概率进行建模

$$
P(x,z) = \prod_{i=1}^{T}P(z_{i}\mid z_{i-1})P(x_{i}\mid z_{i})
$$

可以看到，在第 $t$ 时刻，观测状态仅依赖当前时刻的隐状态，因此在做序列标注任务时，无法考虑上下文信息。

**对HMM进行改进**

$$
P(x,z) = \prod_{i=1}^{T}P(z_{i} \mid z_{i-1})\cdot \underset{计算困难}{ P(x\mid z_{1},z_{2},z_{3},z_{4})}
$$

## 3.2 MEMM（最大熵马尔科夫模型）

根据有向图的D-划分，HMM中 $x_{t-1}$与 $x_{t}$ 是没有关系的（观测独立假设），而在MEMM中，$x_{t-1}$与 $x_{t}$是有关系的。

模型直接对条件概率进行建模：

$$
\begin{eqnarray}
P(z\mid x) &=& \prod_{i=1}^{T}P(z_{i}\mid z_{i-1},x) \tag{1} \\
&=& P(z_{1}|x)P(z_{2}|z_{1},x)P(z_{3}|z_{1},z_{2},x)\cdots P(z_{T}|z_{1},\cdots,z_{T-1},x) \tag{2}
\end{eqnarray}
$$

假设标签的依赖只发生在相对位置，所以

$$
P(z\mid x) = P(z_{1}|x)P(z_{2}|z_{1},x)P(z_{3}|z_{2},x)\cdots P(z_{T}|z_{T-1},x) \tag{3}
$$

仿照线性链CRF的设计，我们可以设计

$$
P(z_{1}|x) = \frac{e^{f(z_{1};x)}}{\sum_{z_{1}}e^{f(z_{1};x)}}, \quad P(z_{t}|z_{t-1},x) = \frac{e^{g(z_{t-1},z_{t}) + f(z_{t};x)}}{\sum_{z_{t}}e^{g(z_{t-1},z_{t}) + f(z_{t};x)}} \tag{4}
$$

将式（4）带入（3）中，得

$$
P(z_{1}|x) = \frac{e^{f(z_{1};x) + g(z_{1},z_{2}) + \cdots + g(z_{T-1},z_{T}) + f(z_{T};x) }}{\color{red}{\left(\sum_{z_{1}}e^{f(z_{1};x)}\right)\left( \sum_{z_{2}}e^{g(z_{1},z_{2}) +f(z_{2};x) } \right)\cdots \left( \sum_{z_{2}}e^{g(z_{T-1},z_{T}) +f(z_{T};x) } \right)}}  \tag{5}
$$

有（5）式可以看出，MEMM与CRF的主要区别就在分母的归一化上，CRF的归一化是全局归一化，而MEMM是局部归一化。

#### Label Bias Problem
[Label Bias Problem](https://awni.github.io/label-bias/)

根本原因在于局部归一化

可以看到

$$
P(2|1,i) = 1 = P(2|1)\\
P(5|4,o) = 1 = P(5|4)
$$

可以看到，从1->2，以及从4->5，等根本没有考虑观测变量。

# 4. CRF

## 4.1 模型表示

#### LogLinear Model

$$
\begin{align}
P(y|x;w) &= \frac{\exp \left[\sum_{j=1}^{J}w_{j}f_{j}(x,y) \right]}{Z(x,w)} \\
Z(x,w) &= \sum_{y} \sum_{j=1}^{J}w_{j}f_{j}(x,y)
\end{align}
$$

#### Linear-CRF

Linear-CRF也属于log-linear model. 用 $\bar{x}, \bar{y}$分别表示输入输出序列

$$
\bar{x} = (x_{1}, x_{2}, \cdots, x_{\mathrm{T}}) \\
\bar{y} = (y_{1}, y_{2}, \cdots, y_{\mathrm{T}})
$$

则，CRF模型可表示为

$$
\begin{align}
P(\bar{y} | \bar{x};w) &= \frac{\exp \left[\sum_{j=1}^{J}w_{j}F_{j}(\bar{x}, \bar{y}) \right] }{Z(\bar{x}, w)} \\
&= \frac{\exp \left[\sum_{j=1}^{J}w_{j} \sum_{i=2}^{\mathrm{T}} f_{j}(y_{i-1}, y_{i}, \bar{x})  \right]} {Z(\bar{x}, w)}
\end{align}
$$

## 4.2 Inference

Given $w, \bar{x}$，求出 $\bar{y}$

$$
\begin{align}
\hat{y} &= \arg \max_{\bar{y}} P(\bar{y} \mid \bar{x};w) \\
&= \arg \max_{\bar{y}} \left( \sum_{j=1}^{J}w_{j} \sum_{i=2}^{\mathrm{T}} f_{j}(y_{i-1}, y_{i}, \bar{x})  \right) \\
&= \arg \max_{\bar{y}} \left( \sum_{i=2}^{\mathrm{T}} \color{MediumOrchid}{ \sum_{j=1}^{J}w_{j} f_{j}(y_{i-1}, y_{i}, \bar{x}) }  \right) \\
&= \arg \max_{\bar{y}} \sum_{i=2}^{\mathrm{T}} \color{MediumOrchid}{ g_{i}(y_{i-1}, y_{i}, \bar{x})  }
\end{align}
$$

使用Viterbi算法求解

假设 $y_{i}$ 的状态有 $m$ 个，则求解的时间复杂度为 $O(m^{2}T)$，空间复杂度为 $O(mT)$

## 4.3 Parameter Estimation

一般形式的log-linear model参数估计

$$
\begin{eqnarray}
\hat{w} &=& \arg \max_{w} P(y |x ;w)  \\
&=& \arg \max_{w} \log P(y |x ;w) \\
&=& \arg \max_{w} \sum_{j=1}^{J} w_{j}F_{j}(x, y) - \log Z(x,w) \tag{1}\\
Z(x,w) &=& \sum_{y^{\prime}} \exp \left[ \sum_{j=1}^{J}w_{j}F_{j}(x,y^{\prime}) \right] \tag{2}
\end{eqnarray}
$$

依次对 $w$ 的各分量求偏导，令导数为0

$$
\frac{\partial \log P(y |x ;w)}{\partial w_{j}} = F_{j}(x, y) - \frac{1}{Z(x,w)}\cdot \boxed{ \frac{\partial}{\partial w_{j}}Z(x,w)} \tag{3}
$$

其中

$$
\begin{align}
\frac{\partial}{\partial w_{j}}Z(x,w) &= \frac{\partial}{\partial w_{j}} \sum_{y^{\prime}} \left[ \exp \sum_{j'}^{J} w_{j'}F_{j'}(x, y') \right] \\
&= \sum_{y^{\prime}} \frac{\partial}{\partial w_{j}} \left[ \exp \sum_{j'}^{J} w_{j'}F_{j'}(x, y') \right] \\
&= \sum_{y^{\prime}} \left(\exp \sum_{j'}^{J} w_{j'}F_{j'}(x, y')\right)\cdot F_{j}(x, y') \tag{4}
\end{align}
$$

将（4）式带入（3）式，可得

$$
\begin{eqnarray}
\frac{\partial \log P(y |x ;w)}{\partial w_{j}} &=& F_{j}(x, y) - \frac{1}{Z(x,w)} \sum_{y^{\prime}} F_{j}(x, y') \left(\exp \sum_{j'}^{J} w_{j'}F_{j'}(x, y')\right) \\
&=& F_{j}(x, y) - \sum_{y^{\prime}} F_{j}(x, y') \frac{\exp \sum_{j'}^{J} w_{j'}F_{j'}(x, y')}{Z(x,w)} \\
&=& F_{j}(x, y) - \sum_{y^{\prime}} F_{j}(x, y') \cdot P(y' | x;w) \\
&=& F_{j}(x, y) - \mathbb{E}_{y' \sim P(y' | x;w)} \left[ F_{j}(x, y') \right] \tag{5}
\end{eqnarray}
$$

## 4.4 Compute Partition Function

归一化因子 $Z(x,w)$ 又称为配分函数。

$$
\begin{align}
Z(\bar{x}, w) &= \sum_{\bar{y}} \exp w_{j}F_{j}(\bar{x}, \bar{y}) \\
&= \sum_{\bar{y}} \exp w_{j} \sum_{i=2}^{\mathrm{T}} f_{j}(y_{i-1},y_{i},\bar{x}) \\
&= \sum_{\bar{y}} \left[ \exp \sum_{i=2}^{\mathrm{T}} g_{i}(y_{i-1},y_{i}) \right ] \tag{6}
\end{align}
$$

如果直接遍历 $\bar{y}$ 所有可能的取值，然后求和，则时间复杂度为$O(m^{\mathrm{T}})$.

为了降低计算时间复杂度，利用动态规划（DP）的思想，有前向算法和后向算法。

#### Forward Computation（前向算法）

定义：

$\alpha(k+1, v)$: `the sum of all possible paths where the tag of time k+1 is v.` 则有递推公式：

$$
\alpha(k+1, v) = \sum_{s} \alpha(k,s)\cdot \exp \left[g_{k+1}(s,v)\right]
$$

最终

$$
Z(\bar{x}, w) = \sum_{v}^{m} \alpha(\mathrm{T}, v)
$$

#### Backward Computation（后向算法）

$$
\beta(u, k) = \sum_{s} \left[ \exp g_{k+1}(u,s) \right]\cdot \beta(s,k+1)
$$

## 4.5 概率值计算

- 计算边缘概率

$$
\begin{align}
P(y_{k}=u \mid \bar{x};w) &= \frac{\alpha(k,u)\beta(u,k)}{Z(\bar{x},w)} \\
Z(\bar{x},w) &= \sum_{u} \alpha(k,u)\beta(u,k)
\end{align}
$$

- 计算相邻两个隐状态的联合概率

$$
\begin{align}
P(y_{k}=u, ~ y_{k+1} = v \mid \bar{x};w) &= \frac{\alpha(k,u)\left[ \exp g_{k+1}(u,v) \right]\beta(v,k+1)}{Z(\bar{x},w)}  \tag{7}
\end{align}
$$

## 4.6 CRF Parameter Estimation

式（5）得到了一般log-linear model的参数估计形式，现在考虑CRF的参数估计。

$$
\begin{align}
\frac{\partial}{\partial w_{j}} \log P(\bar{y} \mid \bar{x};w) &= F_{j}(\bar{x}, \bar{y}) - \mathbb{E}_{y' \sim P(y' | \bar{x};w)} \left[ F_{j}(\bar{x}, y') \right] \\
&= F_{j}(\bar{x}, \bar{y}) - \mathbb{E}_{\bar{y}} \left[ \sum_{i=2}^{T}f_{j}(y_{i-1}, y_{i}, \bar{x}) \right] \\
&= F_{j}(\bar{x}, \bar{y}) - \sum_{i=2}^{T} \mathbb{E}_{\bar{y}} \left[ f_{j}(y_{i-1}, y_{i}, \bar{x}) \right] \\
&= F_{j}(\bar{x}, \bar{y}) - \sum_{i=2}^{T} \mathbb{E}_{y_{i-1}~,y_{i}} \left[ f_{j}(y_{i-1}, y_{i}, \bar{x}) \right] \\
&= F_{j}(\bar{x}, \bar{y}) - \sum_{i=2}^{T} \sum_{y_{i-1}} \sum_{y_{i}} f_{j}(y_{i-1}, y_{i}, \bar{x}) \cdot P(y_{i-1}, y_{i}\mid \bar{x}; w)  \tag{8}\\
&= F_{j}(\bar{x}, \bar{y}) - \sum_{i=2}^{T} \sum_{y_{i-1}} \sum_{y_{i}} f_{j}(y_{i-1}, y_{i}, \bar{x}) \cdot \frac{\alpha(i-1, y_{i-1})\left[ \exp g_{i}(y_{i-1}, y_{i}) \right]\beta(y_{i}, i)}{Z(\bar{x},w)}  \tag{9}
\end{align}
$$