{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Syntax Flavors\n",
"\n",
"> ### In this tutorial we will cover:\n",
"> - the different syntax flavours of building molecules"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you have checked out some of the other tutorials you will have definitely come accross the `bam.connect` function that is used to connect two molecules together. \n",
"However, BuildAMol comprises three different syntaxes to use when performing different tasks. A _functional_, a _method-based_, and an _operator-based_ syntax. \n",
"\n",
"### Functional API\n",
"BuildAMol contains a lot of functions spread over a number of modules. Especially the `structural` module contains a great number of them. Most BuildAMol functions are intended for usage by an end-user, so it is completely fine to import whatever functions you need and use them. This gives a more R-like user experience. Many functions are also automatically imported when loading buildamol. \n",
"\n",
"#### Examples\n",
"- `bam.connect`\n",
"- `bam.read_pdb`\n",
"- `bam.structural.autolabel`\n",
"- `bam.get_compound`\n",
"\n",
"### Method API\n",
"For convenience, most functions that a user is likely to use on a regular basis have already been integrated into methods of the `Molecule` or other classes. Hence, it is probably the most convenient for most users to rely on the method based API the most since it saves you the time of importing the modules necessary. Most methods support the full range of parameters that the functions they are linked to support, but this is not always the case! Of course, there are many methods that are only implemented as methods and not available as stand-alone functions. Additionally, there are some synonymous methods available such as `bam.Molecule.get_residue_graph` and `bam.Molecule.make_residue_graph` for historic reasons and compatibility in the code base.\n",
"\n",
"#### Examples\n",
"- `bam.Molecule.attach`\n",
"- `bam.Molecule.from_pdb`\n",
"- `bam.Molecule.autolabel`\n",
"- `bam.PDBEComponds.get`\n",
"\n",
"### Operator API\n",
"Operators are a great way of writing very short code. BuildAMol implements a syntax called \"molecular arithmetics\" that supports the basic operations for connecting molecules together. For instance, it allows us to connect `mol_c = mol_a + mol_b`. Naturally, this syntax is the most constrained out of the three, but it offers a wonderfully short way of creating larger structures. \n",
"\n",
"#### Available Operators\n",
"| Function | Method | Attribute | Operator |\n",
"|------------------------------------------------|----------------------------------------------------|-----------------------------------------|--------------------------|\n",
"| - | `mol_a.set_linkage(link)` | `mol_a.linkage = link` | `mol_a % link`\n",
"| - | `mol_a.set_attach_residue(res)` | `mol_a.attach_residue = res` | `mol_a @ res` |\n",
"| - | `mol_a.set_root(root_atom)` | `mol_a.root_atom = root_atom` | `mol_a ^ root_atom` |\n",
"| `mol_c = bam.connect(mol_a, mol_b, link)` | `mol_c = mol_a.attach(mol_b, link, inplace=False)` | - | `mol_c = mol_a + mol_b` |\n",
"| `bam.connect(mol_a, mol_b, link, copy_a=False)` | `mol_a.attach(mol_b, link)` | - | `mol_a += mol_b`\n",
"| `mol_c = bam.polymerize(mol_a, n, link)` | `mol_c = mol_a.repeat(n, link, inplace=False)` | - | `mol_c = mol_a * n` |\n",
"| `bam.polymerize(mol_a, n, link, inplace=True)` | `mol_a.repeat(n, link)` | - | `mol_a *= n` |\n",
"\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> ### A general note on functions versus methods\n",
"> The functional API is usually taylored toward **non-inplace** operations, while the method based API is taylored toward **inplace** operations. If you look at the table above closely, you will notice that the `copy` and `inplace` arguments are always switched between the two. So, when calling `Molecule.attach`, the operation will be in-place by default, while calling `connect` will by default return a copy."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Examples"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's look at an example. Because we like sugars so much, we'll build a glycan structure (yep, not very creative, but it does the trick)... If you are unfamiliar with glycans, just tag along for the ride and don't think too much about it. It's just an example...\n",
"\n",
"```mermaid\n",
"flowchart TB\n",
" node_1[\"Glucose\"]\n",
" node_2[\"Glucose\"]\n",
" node_3[\"Galactose\"]\n",
" node_4[\"Galactose\"]\n",
" node_5[\"Galactose\"]\n",
" node_6[\"Mannose\"]\n",
" node_7[\"Mannose\"]\n",
" node_8[\"Glucose\"]\n",
" node_1 --\"beta 1-4\"--> node_2\n",
" node_2 --\"alpha 1-3\"--> node_3\n",
" node_3 --\"alpha 1-4\"--> node_4\n",
" node_4 --\"alpha 1-3\"--> node_5\n",
" node_6 --\"beta 1-4\"--> node_7\n",
" node_7 --\"alpha 1-4\"--> node_8\n",
" node_2 --\"beta 1-2\"--> node_6\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
" \n",
" "
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import plotly\n",
"plotly.offline.init_notebook_mode()"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "8128ab15094045078860deabd999caa1",
"version_major": 2,
"version_minor": 0
},
"text/plain": []
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import buildamol as bam\n",
"# first get some compounds\n",
"bam.load_sugars()\n",
"\n",
"glc = bam.molecule(\"GLC\")\n",
"gal = bam.molecule(\"GAL\")\n",
"man = bam.molecule(\"MAN\")\n",
"fuc = bam.molecule(\"FUC\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Using functional syntax\n",
"\n",
"Now we will build the glycan only using the functional syntax of BuildAMol:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/noahhk/anaconda3/envs/glyco2/lib/python3.11/site-packages/plotly/express/_core.py:1985: FutureWarning:\n",
"\n",
"When grouping with a length-1 list-like, you will need to pass a length-1 tuple to get_group in a future version of pandas. Pass `(name,)` instead of `name` to silence this warning.\n",
"\n"
]
},
{
"data": {
"application/vnd.plotly.v1+json": {
"config": {
"plotlyServerURL": "https://plot.ly"
},
"data": [
{
"customdata": [
[
"C1",
1,
1,
"GLC",
"A"
],
[
"C2",
2,
1,
"GLC",
"A"
],
[
"C3",
3,
1,
"GLC",
"A"
],
[
"C4",
4,
1,
"GLC",
"A"
],
[
"C5",
5,
1,
"GLC",
"A"
],
[
"C6",
6,
1,
"GLC",
"A"
],
[
"C1",
24,
2,
"GLC",
"A"
],
[
"C2",
25,
2,
"GLC",
"A"
],
[
"C3",
26,
2,
"GLC",
"A"
],
[
"C4",
27,
2,
"GLC",
"A"
],
[
"C5",
28,
2,
"GLC",
"A"
],
[
"C6",
29,
2,
"GLC",
"A"
],
[
"C1",
44,
3,
"GAL",
"A"
],
[
"C2",
45,
3,
"GAL",
"A"
],
[
"C3",
46,
3,
"GAL",
"A"
],
[
"C4",
47,
3,
"GAL",
"A"
],
[
"C5",
48,
3,
"GAL",
"A"
],
[
"C6",
49,
3,
"GAL",
"A"
],
[
"C1",
65,
4,
"GAL",
"A"
],
[
"C2",
66,
4,
"GAL",
"A"
],
[
"C3",
67,
4,
"GAL",
"A"
],
[
"C4",
68,
4,
"GAL",
"A"
],
[
"C5",
69,
4,
"GAL",
"A"
],
[
"C6",
70,
4,
"GAL",
"A"
],
[
"C1",
86,
5,
"GAL",
"A"
],
[
"C2",
87,
5,
"GAL",
"A"
],
[
"C3",
88,
5,
"GAL",
"A"
],
[
"C4",
89,
5,
"GAL",
"A"
],
[
"C5",
90,
5,
"GAL",
"A"
],
[
"C6",
91,
5,
"GAL",
"A"
],
[
"C1",
108,
6,
"MAN",
"A"
],
[
"C2",
109,
6,
"MAN",
"A"
],
[
"C3",
110,
6,
"MAN",
"A"
],
[
"C4",
111,
6,
"MAN",
"A"
],
[
"C5",
112,
6,
"MAN",
"A"
],
[
"C6",
113,
6,
"MAN",
"A"
],
[
"C1",
129,
7,
"MAN",
"A"
],
[
"C2",
130,
7,
"MAN",
"A"
],
[
"C3",
131,
7,
"MAN",
"A"
],
[
"C4",
132,
7,
"MAN",
"A"
],
[
"C5",
133,
7,
"MAN",
"A"
],
[
"C6",
134,
7,
"MAN",
"A"
],
[
"C1",
150,
8,
"GLC",
"A"
],
[
"C2",
151,
8,
"GLC",
"A"
],
[
"C3",
152,
8,
"GLC",
"A"
],
[
"C4",
153,
8,
"GLC",
"A"
],
[
"C5",
154,
8,
"GLC",
"A"
],
[
"C6",
155,
8,
"GLC",
"A"
]
],
"hovertemplate": "atom_element=C
x=%{x}
y=%{y}
z=%{z}
__marker_size=%{marker.size}
atom_id=%{customdata[0]}
atom_serial=%{customdata[1]}
residue_serial=%{customdata[2]}
residue_name=%{customdata[3]}
chain_id=%{customdata[4]}
x=%{x}
y=%{y}
z=%{z}
__marker_size=%{marker.size}
atom_id=%{customdata[0]}
atom_serial=%{customdata[1]}
residue_serial=%{customdata[2]}
residue_name=%{customdata[3]}
chain_id=%{customdata[4]}
x=%{x}
y=%{y}
z=%{z}
__marker_size=%{marker.size}
atom_id=%{customdata[0]}
atom_serial=%{customdata[1]}
residue_serial=%{customdata[2]}
residue_name=%{customdata[3]}
chain_id=%{customdata[4]}