Antigen-antibody binding is one of the key steps of an immune response. However, when an antibody mistakenly binds to a human protein, it can lead to a variety of autoimmune diseases. This presents a serious risk factor for synthetic antibodies used in medications. Hence, it is crucial to create antibody structures that are not complementary to human molecules. Given a list of human and non-human proteins, this project creates an antibody with high affinity for the foreign proteins but low affinity for the native proteins. Computational methods such as creating a mesh from 3D plots and minimizing functions using machine learning are used to analyze geometric structures.

Introduction

Antibody-antigen binding is one of the key steps of an immune response. An antigen is any foreign substance in the immune system, such as viruses and bacteria. When an antigen enters the immune system, specialized white blood cells such as B-cells and T-cells bind to it. In particular, B-cells have proteins called antigen receptors on their surface, which bind to complementary antigens. Once a B-cell attaches to an antigen, it starts a process that secretes a soluble form of its antigen receptor: an antibody. The antibody has a unique shape specific for the antigen, allowing it to bind to similar structures in the future, helping fend off foreign invaders.
​
Normally, an antibody can distinguish between foreign and human molecules. However, when an antibody recognizes a native molecule as an antigen, it can trigger a false immune response. Such a false flag can result in a variety of autoimmune diseases, including rheumatoid arthritis (causing joint damage), Type 1 diabetes, and multiple sclerosis (leading to cognitive issues) (Cornaby et al., 2015). This is especially a concern as many drugs utilize synthetic antibodies called biologics. In recent years, the rise of therapeutic antibodies has been explosive, and they are prominent in treating cancerous, inflammatory, and autoimmune diseases (Sharma et al., 2023). If these synthetic antibodies accidentally bind to human molecules, they can trigger autoimmune conditions.
​
Shape complementarity is essential for antigen-antibody binding. Antibodies bind to antigens via noncovalent bonds, such as electrostatic interactions, Van der Waals forces, and hydrogen bonds (Janeway et al., 2001). Thus, a perfect fit is not required; if the shapes are sufficiently compatible, bonds can form. This means a good metric for geometric complementarity is the volume between the antigen and antibody—minimizing volume leads to stronger affinity.
​
While there have been efforts to geometrically predict binding sites in protein-protein binding (Milanetti et al., 2021), there has been relatively little focus on making antibody structures such that they do not bind to human molecules. Thus, this project uses computational tools to create an optimal antibody structure with both high affinity for non-human proteins and low affinity for human proteins. Because there are infinitely many possible antibodies, a machine learning approach is necessary to determine the optimal structure. This essay uses Bayesian optimization, which is suited for optimizing functions that take a long time to evaluate (Frazier, 2018).

Importing Proteins

The Protein Data Bank (PDB) is a global database of the 3D structures of biomolecules like proteins and nucleic acids. I imported five human and five non-human proteins from the PDB, chosen for their relative structural simplicity.
Lists of proteins:
In[]:=
humanProteins={"1PPT","1UBQ","1LZ1","3I40","2HHB"};​​nonHumanProteins={"1DPX","1OVA","6M0J","3SEB","1AF9"};
Some proteins here are noteworthy. The list of human proteins includes 1UBQ, a fundamental regulatory protein found in the human body, and 3I40, which is human insulin. These are central human proteins that should not attach to the antibody. Meanwhile, in the list for non-human proteins, 6M0J represents the Coronavirus spike protein’s receptor-binding domain. 3SEB is an antigen known for its high toxicity (Crawford, 2018). Thus, a good antibody would be able to bind to these dangerous proteins with no harm to fundamental human processes.
Grabbing molecule from PDB:
In[]:=
pdbMolecule[s_]:=BioMolecule[ExternalIdentifier["PDBStructureID",s]]

Converting Protein 3D Plot into a 2D Polygon

Before tackling the problem in 3D, I first solved its less complicated 2D version. I turned the protein into a 3D mesh, which breaks down the complex, continuous protein into simple, discrete shapes. Then, I projected the mesh onto a 2D plane, and reduced it to a polygon.
I depicted the steps with the protein 1UBQ.
Package necessary for mesh generation:
In[]:=
Needs["NDSolve`FEM`"];
Wolfram has multiple biomolecule plot themes. I used the solvent-accessible surface model, which is relevant because I am investigating where the antibody can bind.
BioMoleculePlot3D[pdbMolecule["1UBQ"],PlotTheme->"SolventAccessibleSurface"]
Out[]=
First, DiscretizeGraphics converts the 3D plot into a mesh:
In[]:=
spaceMesh[protein_]:=DiscretizeGraphics[BioMoleculePlot3D[pdbMolecule[protein],PlotTheme->"SolventAccessibleSurface"][[1]][[3]][[1,1]][[3]]]
Demonstration for 1UBQ:
spaceMesh["1UBQ"]
Out[]=
Second, ElementMeshProjection projects the 3D mesh onto the xy plane:
In[]:=
planeMesh[protein_]:=​​Module[{projectedProtein},​​projectedProtein=ElementMeshProjection[ToBoundaryMesh[spaceMesh[protein]],{#[[1]],#[[2]]}&];​​ImageMesh[ColorNegate@Rasterize@projectedProtein["Wireframe"["MeshElementStyle"->FaceForm[Black]]],Method->"LinearSeparable"]​​]
In[]:=
planeMesh["1UBQ"]
Out[]=
Finally, I turned the 2D mesh into a polygon to analyze it geometrically. I took only the tenth points of the polygon to reduce complexity, while retaining its overall shape.
Use MeshPrimities to extract vertices, and take only the tenth points:
In[]:=
polygonize[protein_]:=Module[{silhouettePoints,curatedPoints},​​silhouettePoints=MeshPrimitives[planeMesh[protein],0];​​curatedPoints=Flatten@Table[If[Divisible[ptIndex,10],silhouettePoints[[ptIndex]],{}],{ptIndex,Length@silhouettePoints}];​​Polygon[Table[curatedPoints[[i]][[1]],{i,Length@curatedPoints}]]​​]
Testing for 1UBQ:
In[]:=
Region[(examplePolygon=polygonize["1UBQ"])]
Out[]=

Creating 2D Models of Antibodies Centered at each Vertex

I then worked solely with the 2D polygon. There are largely two geometric conditions for a good antibody. First, the area between the antibody and antigen should be small to allow bonds to form. Second, the two molecules should “click” together. Touching each other’s surface is not enough; the molecules should have a lock-and-key fit. In the geometric model, this means finding binding sites where the complementary shapes are angularly aligned.
​
To achieve this, I centered the antibodies at the angle bisector of each vertex. I modeled the antibody as a polygon with a base that is symmetric about the angle bisector. Thus, I first obtained all angle bisectors, and then created segments perpendicular to them to serve as the antibody bases.

Getting angle bisectors and perpendicular segments

First, I obtain the polygon coordinates in order.
RegionBoundary gives a Line object, where the first vertex appears both first and last:
In[]:=
coord[polygon_]:=RegionBoundary[Region[polygon]][[1]]
Thus, the number of polygon vertices is one less than the length of coord.
Now, I make a list of all angle bisectors:
angleBisectors[polygon_]:=Module[{coordinates=coord[polygon]},​​Table[​​AngleBisector[​​{coordinates[[Mod[i-1,Length@coordinates-1,1]]],​​coordinates[[i]],​​coordinates[[Mod[i+1,Length@coordinates-1,1]]]}​​],{i,Length@coordinates-1}​​]]
To take segments perpendicular to each angle bisector, I need to know their slope. As Wolfram does not have a built-in slope function, I created helper functions.
Angle bisectors have InfiniteLine, so I use infiniteSlope on them:
Slope of a line:
Now, I determine segments perpendicular to the angle bisectors; they will form the base of the antibodies. Define the midpoint of a perpendicular segment as its critical point.
Increment for position of critical point, and length of each perpendicular segment, respectively:
Incrementing the critical point and choosing the first one for which the perpendicular segment does not cross the polygon:
Creating perpendicular segments:
Visualizing the perpendicular segments for 1UBQ:

Creating 2D models of antibodies

I divide each perpendicular segment into n sub-segments. At each partition point, I take the perpendicular to the segment, and choose its first intersection with the polygon sides. The intersection points will form the vertices of the antibody.
Random small value for n:
Getting sides of a polygon from its coordinates:
Partitioning each perpendicular segment:
Computing the height at each partition point:
Making antibody vertices:
Creating the shape:
Visualizing an antibody binding at one corner:

Finding the Best-fitting Antibody

I now create 2D models of antibodies with a high affinity for non-human proteins but a low affinity for human proteins. An antibody is modeled as a polygon with a straight base and vertices on the perpendiculars at each partition point.
A cost function in machine learning measures the difference between a model’s predictions and the true data. Cost functions allow a model to determine if a change it has made is beneficial or disadvantageous.
I create a cost function for a single protein, which measures the difficulty for a particular antibody to bind to the protein. I define it as the minimal area gap between a specific antibody and an antigen, over all corners of the antigen. As the partition points are evenly divided, the area gap is roughly proportional to the sum of the distance gaps over all partition points.
Cost for just one vertex:
I make the cost function logarithmic in nature (x^1/3). Then, the cost function decreases more steeply for lower values, e.g., from x=1 to x=0 compared with x=100 to x=99. This maximizes the number of close antibody-antigen fits.
Minimal cost over all vertices:
I then create a master cost function, which is the sum of all costs for non-human proteins, minus the the sum of all costs for human proteins.
Master cost function:
BayesianMinimization is a process in which a set of values is modified on increasingly small scales to find minima in a function. Though it has the disadvantage of sometimes falling into local minima, it is ideal for black-box functions like our antibody-antigen cost method.
Using BayesianMinimization on the master costFunction yields a list of n+1 values for the heights of the optimal antibody. The upper bound of perpLength/2 is chosen loosely.
The range over which BayesianMinimization computes is given as a cuboid:
Storing the results for one particular run:
I now visualize this antibody. For each protein, I find the best place it can bind by calculating the distance between the antibody and the protein at each vertex and choosing the location with the smallest such measure. This allows us to see, under optimal conditions, if the antibody is able to sufficiently differentiate between types of proteins.
Optimal binding site where the cost is the lowest:
Shape at optimal binding site, with the offset ensuring the antibody doesn’t pierce the antigen:
Depicting non-human protein binding:
Depicting human protein binding:
Observe that the antibody and antigen are closer for non-human proteins, as expected. I noted, however, that the visual difference was not as significant as I had hoped. In moving to 3D, the extra dimensions may allow a more striking contrast in shape fitting.

Setting Up Mesh for 3D

I now move from 2D to 3D. In three dimensions, the model more accurately reflects the actual chemical binding that occurs in antibody-antigen interactions.
In three dimensions, I use the protein 1PPT as a prototype for visualizations. Note how the raw mesh has too many vertices, which is not good for compute time:
Thus, I cap the number of vertices at 1250, which still retains the overall shape.
SimplifyMesh reduces the complexity of the mesh:
Visualizing the simplified mesh for 1PPT:

Converting to 3D

Making basic mesh functions

Getting the vertices:
Take a particular vertex, which I will use for visualizations:
Getting the faces:
Getting the planes incident to a vertex:

Creating angle-weighted vertex normal

While a 2D vertex has two incident edges, a 3D vertex has many incident faces. I wish to implement a 3D equivalent of the angle bisector. The standard approach is to average the normals of the faces incident to a vertex, but Thürrner and Wüthrich (1998) created an angle-weighted version of the vertex normal. This version best recreates the concept of an angle bisector.
I implement the following formula for angle-weighted vertex normals.
faceNormal gives the unit normal of a face (N_i):
vertexAngle gives the vertex angles (alpha_i):
The final formula is
Demonstration for one vertex:

Taking perpendicular planes at the normal

I base the antibodies on square planes perpendicular to each vertex normal. Define the center of each square as its central point.
Increment for central point, and side length:
Before making the planes, I create helper functions to create a square on a plane. Set the vertices of the square so that they lie on the orthogonal vectors of the normal.
NullSpace gives the orthogonal basis, and Orthogonalize makes it orthonormal:
Creating a square with specific sideLength, centered at a point, with two vertex vectors:
I want to select a plane so that it does not intersect with the antigen. To be safe, I set the planes to be at least a certain distance (clearance) away from the mesh:
But first, I need to guarantee that the plane is positioned outwards from the mesh. I designate a normal vector that points outwards from the mesh. The normal points outward if it forms an acute angle with the vector from the mesh centroid to the vertex, i.e., if their dot product is positive.
Normal vector directed outwards from the mesh:
Now I can increment the central point on the outward normal vector, and choose the first one for which the corresponding square plane does not intersect the mesh.
Central point:
Square plane:
Visualizing square plane for a particular vertex:

Creating 3D models of antibodies

I divide each perpendicular plane into n^2 sub-coordinates, at which I will take heights to the antigen.
Arbitrary small value:
n^2 partition coordinates:
Visualizing partition points:
The previous approach was to intersect all faces with each perpendicular. However, that takes too long because Region functions are generally expensive.
​
Instead, I utilize EuclideanDistance by taking the centroid of each face, measuring its distance to the normal at a partition point, and choosing the closest one. I define the distance from that centroid to the partition point as the height.
Centroid function:
Normal vector along the line from v1 to v2, which I will use later:
For each partition point, I only consider centroids within an angle restrictTheta from the normal vector:
I now create a function that outputs the optimal antibody heights. To aid understanding, I present the following diagram, which is a 2D cross section of the situation.
P is a partition point, A and B are points on the mesh, G is the centroid of the mesh, and the dotted lines form 15 Degrees with GP:
Assume that point A is closest and point B is second closest to the normal at P. Although point A is closest, it is not within 15 degrees of GP. Meanwhile, point B is the second closest, and it is within 15 degrees of GP. Thus, I take the length of PB as the height at P.
​
In particular, I formulate the 15 degree condition using dot products. Let theta be the angle formed by GP and GX, where X is a mesh point.
Condition I use in the following function to restrict angle to restrictTheta:
Implementing the math:
Taking vertices:
Now, I create a shape. For stylistic reasons, I take the tube (cylinder) at each partition point up to the heights.
Antibody shape:
Visualizing for a specific vertex of 1PPT:

Creating cost functions

I input an n by n array of heights.
I first create a cost function for a single protein, defined as the minimal volume gap between a specific antibody and the antigen, over all vertices of the antigen. Because the base of each tube is a circle of equal radius, the volume is proportional to the sum of heights.
Cost for one corner (the offset allows for a uniform comparison between proteins):
Cost for one protein (I constrain the run time for each corner to five seconds):
Master cost function for all proteins, with a raw input of n^2 heights:
Finally, I run BayesianMinimization on the master cost function. The upper bound for each height is chosen loosely as sideLength.
n^2 heights for the optimal antibody:
Here is the result for a particular run:
The optimal height array:
The visualization of the antibody, at the moment, is blocky and cylindrical because only 16 sampling points are optimized. By passing these heights into a smoothing function, it is possible to approximate what the actual surface of the antibody might look like.
ListPlot representation of antibody:

Finding optimal binding site for the best-fitting antibody

It remains to visualize the optimal antibody binding to the best spots of each of the ten proteins. First, I find the best binding site. I constrain the cost function run time for each vertex to five seconds; this reduces computation time while keeping most vertices.
Running cost function over all vertices and taking the vertex that minimizes it:
Antibody vertices at optimal binding site:
I now designate a shape for the antibody. I make it an array of tubes, purely for stylistic reasons.
Antibody shape at optimal binding site :

Results

To observe the results of the Bayesian minimization, I visualized the antibody at its ideal binding sites in much the same way as I did for 2D.
I first converted all proteins into polyhedra.
Next, I depicted the protein mesh and an antibody binding at the optimal site.
Graphics, with mesh opacity 0.5 to see if the antibody pierces through the antigen:
Visualizing non-human protein binding:
Visualizing human protein binding:
I encourage the reader to rotate the graphics and compare the binding affinity for human and non-human proteins. It is visually clear that the antibody binds far well with non-human proteins.
Now, I quantitatively compare the binding affinity for all ten proteins.
Getting the cost function output:
Creating rows for human and non-human antigens:
Creating table:
Note that the scores (costs) for human proteins are larger than those for non-human proteins.

A Happy Accident

But, significantly, I made a mistake in my initial classification of proteins. 1PPT is actually not a human protein. While this doesn’t affect the validity of the methodology, it does affect the validity of our final antibody. However, this raised a question:
Is the optimal antibody specialized in just these 10 proteins, or is it a generalized differentiator between human and non-human proteins? To answer this question, I created a box plot of the scores with and without 1PPT.
Box plot, where green is human and red is non-human:
The median score for human proteins without 1PPT (89.21) is higher than the median score with 1PPT (87.51). This result indicates that the antibody is worse at binding to the actual human proteins than 1PPT. In turn, this hints at a structural difference between human and non-human proteins, and that the antibody I found might be generalizable to proteins outside the ten I selected.
Ultimately, with or without 1PPT, the scores (costs) for human proteins are significantly larger than those for non-human proteins. This demonstrates that the Bayesian optimization has successfully found a shape that can differentiate between foreign and native molecules.

Conclusion

This project created an optimal 3D antibody shape that effectively binds to a set of non-human proteins but has low affinity for a set of human proteins. The antibody was modeled as an array of tubes with a square base. Many optimizations were made to reduce computation time. In particular, the maximum number of vertices in the protein 3D mesh was set to 1250, and n was set to 4. A more fine-grained protein mesh and a larger value of n would make the model even more precise.
​
It is important to note that the antibody is modeled solely based on geometric complementarity. Hence, such a structure would likely not be able to form naturally, because amino acids cannot fold that way. However, this approach has a high potential in synthetic antibodies, which are produced in vitro and thus can assume virtually any shape.

Future Directions

There are multiple ways to expand this project. In 2D, the position at which the 3D mesh is projected onto the xy plane is arbitrary. Centering proteins on a particular axis will allow for uniform projections across all proteins, enabling comparison of different proteins. Of course, this applies only to 2D, and is unnecessary for practical 3D work.
Another direction is to explore different types of cost functions. I used the cube root function, but there are other functions that are logarithmic in nature, and certain ones may yield better antibodies.
A critical bottleneck in this project was compute time. Even after multiple optimizations, such as using Module and replacing Region functions with EuclideanDistance, the 3D BayesianMinimization took 4.5 hours. More compute power would allow for more fine-grained antibody models.

Acknowledgements

I would like to thank my mentor, William Choi-Kim, for his expert guidance throughout the project. His insights on cost functions and reducing computation time were particularly enlightening. I also thank Stephen Wolfram for suggesting this project based on my interests; it really introduced me to the beauty of mathematical biology! Moreover, I appreciate my teaching assistant, Max Wang, for providing feedback on this essay. Finally, I am grateful to all the TAs and program directors who made this camp such a fun experience.

References

◼
  • Cornaby, C., Gibbons, L., Mayhew, V., Sloan, C. S., Welling, A., & Poole, B. D. (2015). B cell epitope spreading: Mechanisms and contribution to autoimmune diseases. Immunology Letters, 163(1), 56–68. https://doi.org/10.1016/j.imlet.2014.11.001
  • ◼
  • Sharma, P., Joshi, R. V., Pritchard, R., Xu, K., & Eicher, M. A. (2023). Therapeutic antibodies in medicine. Molecules, 28(18), 6438. https://doi.org/10.3390/molecules28186438
  • ◼
  • Janeway, C. A., Jr., Travers, P., Walport, M., & Shlomchik, M. J. (2001). The interaction of the antibody molecule with specific antigen. In Immunobiology: The Immune System in Health and Disease. 5th edition. Garland Science. https://www.ncbi.nlm.nih.gov/books/NBK27160/
  • ◼
  • Milanetti, E., Miotto, M., Di Rienzo, L., Monti, M., Gosti, G., & Ruocco, G. (2021). 2D Zernike polynomial expansion: Finding the protein-protein binding regions. Computational and Structural Biotechnology Journal, 19, 29–36. https://doi.org/10.1016/j.csbj.2020.11.051
  • ◼
  • Frazier, P. I. (2018, July 8). A tutorial on bayesian optimization. arXiv.Org. https://arxiv.org/abs/1807.02811v1
  • ◼
  • Crawford, K. (2018, July 11). Staphylococcal Enterotoxin Type B (SEB) Toxin in Research. List Labs. https://listlabs.com/seb-toxin-research/
  • ◼
  • Thürrner, G., & Wüthrich, C. A. (1998). Computing vertex normals from polygonal facets. Journal of Graphics Tools, 3(1), 43–46. https://doi.org/10.1080/10867651.1998.10487487
  • CITE THIS NOTEBOOK

    Optimizing 3D geometry for a universal antibody​
    by Donghyeon Kim​
    Wolfram Community, STAFF PICKS, July 9, 2026
    ​https://community.wolfram.com/groups/-/m/t/3751873