Permalink
Browse files

added geometry parities for 2D/3D double bond configurations and some…

… utility classes for combinding their permutation parities

Signed-off-by: Egon Willighagen <egonw@users.sourceforge.net>
  • Loading branch information...
johnmay authored and egonw committed Feb 18, 2013
1 parent 5640f29 commit 37ca197ad7482772aa3e2a957ee6a4fd87407827
@@ -43,15 +43,16 @@
* Create a permutation parity for the provided indices.
*
* @param indices sub-array of indices
* @throws NullPointerException the provided indices were null
* @throws NullPointerException the provided indices were null
* @throws IllegalArgumentException less then two indices provided
*/
@TestMethod("testConstruction_Null,testConstruction_Empty")
public BasicPermutationParity(int[] indices) {
if (indices == null)
throw new NullPointerException("no indices[] provided");
if (indices.length < 2)
throw new IllegalArgumentException("at least 2 incides required");
throw new IllegalArgumentException("at least 2 incides required," +
"use PermutationParity.IDENTITY for single neighbors");
this.indices = indices;
}
@@ -0,0 +1,38 @@
package org.openscience.cdk.hash.stereo.parity;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
/**
* Combine two permutation parities into one.
*
* @author John May
* @cdk.module hash
*/
@TestClass("org.openscience.cdk.hash.stereo.parity.CombinedPermutationParityTest")
public final class CombinedPermutationParity implements PermutationParity {
private final PermutationParity left;
private final PermutationParity right;
/**
* Combines the left and right parity into a single parity. This parity is
* the product of the two separate parities.
*
* @param left either parity
* @param right other parity
*/
public CombinedPermutationParity(PermutationParity left, PermutationParity right) {
this.left = left;
this.right = right;
}
/**
* @inheritDoc
*/
@TestMethod("testParity")
@Override public int parity(long[] current) {
return left.parity(current) * right.parity(current);
}
}
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2013. John May <jwmay@users.sf.net>
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* All we ask is that proper credit is given for our work, which includes
* - but is not limited to - adding the above copyright notice to the beginning
* of your source code files, and to any copyright notice that you may distribute
* with programs based on this work.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U
*/
package org.openscience.cdk.hash.stereo.parity;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
import javax.vecmath.Point2d;
/**
* Calculate the geometric configuration of a double bond. The configuration is
* provided as a parity (+1,0,-1) where +1 indicates the substituents are on
* <i>opposite</i> sides (E or trans) and -1 indicates they are <i>together</i>
* on the same side (Z or cis). If one of the substituents is parallel to the
* double bond then the configuration is unspecified and 0 is returned.
*
* @author John May
* @cdk.module hash
*/
@TestClass("org.openscience.cdk.hash.stereo.parity.DoubleBond2DParityTest")
public final class DoubleBond2DParity implements GeometricParity {
// coordinates of the double bond atoms
// x w
// \ /
// u = v
private Point2d u, v, x, w;
/* the area below which we return unspecified parity */
private static final double THRESHOLD = 0.1;
/**
* Create a new double bond parity for the 2D coordinates of the atoms.
*
* @param left one atom of the double bond
* @param right the other atom of a double bond
* @param leftSubstituent the substituent atom connected to the left atom
* @param rightSubstituent the substituent atom connected to the right atom
*/
public DoubleBond2DParity(Point2d left, Point2d right,
Point2d leftSubstituent, Point2d rightSubstituent) {
this.u = left;
this.v = right;
this.x = leftSubstituent;
this.w = rightSubstituent;
}
/**
* Calculate the configuration of the double bond as a parity.
*
* @return opposite (+1), together (-1) or unspecified (0)
*/
@TestMethod("opposite,together,unspecified")
@Override public int parity() {
return parity(x, u, v) * parity(w, v, u);
}
/**
* Determine the rotation parity of one side of the double bond. This parity
* is the sign of the area of a triangle.
*
* <pre>
* a
* \
* b = c
* </pre>
*
* @param a coordinates of the substituent atom
* @param b coordinates of the atom next to the substituent
* @param c coordinates of the atom double bonds to <i>b</i>
* @return clockwise (+1), anti-clockwise (-1) or unspecified (0)
*/
private static int parity(Point2d a, Point2d b, Point2d c) {
double det = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
return Math.abs(det) < THRESHOLD ? 0 : (int) Math.signum(det);
}
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2013. John May <jwmay@users.sf.net>
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* All we ask is that proper credit is given for our work, which includes
* - but is not limited to - adding the above copyright notice to the beginning
* of your source code files, and to any copyright notice that you may distribute
* with programs based on this work.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U
*/
package org.openscience.cdk.hash.stereo.parity;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
import javax.vecmath.Point3d;
/**
* Calculate the geometric configuration of a double bond. The configuration is
* provided as a parity (+1,-1) where +1 indicates the substituents are on
* <i>opposite</i> sides (E or trans) and -1 indicates they are <i>together</i>
* on the same side (Z or cis).
*
* @author John May
* @cdk.module hash
*/
@TestClass("org.openscience.cdk.hash.stereo.parity.DoubleBond3DParityTest")
public final class DoubleBond3DParity implements GeometricParity {
// coordinates of the double bond atoms:
// x w
// \ /
// u = v
private Point3d u, v, x, w;
/**
* Create a new double bond parity for the 2D coordinates of the atoms.
*
* @param left one atom of the double bond
* @param right the other atom of a double bond
* @param leftSubstituent the substituent atom connected to the left atom
* @param rightSubstituent the substituent atom connected to the right atom
*/
public DoubleBond3DParity(Point3d left, Point3d right,
Point3d leftSubstituent, Point3d rightSubstituent) {
this.u = left;
this.v = right;
this.x = leftSubstituent;
this.w = rightSubstituent;
}
/**
* Calculate the configuration of the double bond as a parity.
*
* @return opposite (+1), together (-1)
*/
@TestMethod("opposite,together")
@Override public int parity() {
// create three vectors, v->u, v->w and u->x
double[] vu = toVector(v, u);
double[] vw = toVector(v, w);
double[] ux = toVector(u, x);
// normal vector (to compare against), the normal vector (n) looks like:
// x n w
// \ |/
// u = v
double[] normal = crossProduct(vu, crossProduct(vu, vw));
// compare the dot products of v->w and u->x, if the signs are the same
// they are both pointing the same direction. if a value is close to 0
// then it is at pi/2 radians (i.e. unspecified) however 3D coordinates
// are generally discrete and do not normally represent on unspecified
// stereo configurations so we don't check this
int parity = (int) Math.signum(dot(normal, vw))
* (int) Math.signum(dot(normal, ux));
// invert sign, this then matches with Sp2 double bond parity
return parity * -1;
}
/**
* Create a vector by specifying the source and destination coordinates.
*
* @param src start point of the vector
* @param dest end point of the vector
* @return a new vector
*/
private static double[] toVector(Point3d src, Point3d dest) {
return new double[]{dest.x - src.x,
dest.y - src.y,
dest.z - src.z};
}
/**
* Dot product of two 3D coordinates
*
* @param u either 3D coordinates
* @param v other 3D coordinates
* @return the dot-product
*/
private static double dot(double[] u, double[] v) {
return (u[0] * v[0]) + (u[1] * v[1]) + (u[2] * v[2]);
}
/**
* Cross product of two 3D coordinates
*
* @param u either 3D coordinates
* @param v other 3D coordinates
* @return the cross-product
*/
private static double[] crossProduct(double[] u, double[] v) {
return new double[]{(u[1] * v[2]) - (v[1] * u[2]),
(u[2] * v[0]) - (v[2] * u[0]),
(u[0] * v[1]) - (v[0] * u[1])};
}
}
@@ -29,10 +29,22 @@
*
* @author John May
* @cdk.module hash
* @see <a href="http://en.wikipedia.org/wiki/Parity_of_a_permutation">Parity of a Permutation, Wikipedia</a>
* @see <a href="http://en.wikipedia.org/wiki/Parity_of_a_permutation">Parity of
* a Permutation, Wikipedia</a>
*/
public interface PermutationParity {
/**
* Identity parity which always returns 1 (even). This is useful for
* configurations which do not require ordering, such as, double bonds with
* implicit hydrogens.
*/
public static final PermutationParity IDENTITY = new PermutationParity() {
@Override public int parity(long[] current) {
return 1;
}
};
/**
* Calculate the permutation parity of a permutation on the current values.
* The inversion parity counts whether we need to do an odd or even number
@@ -0,0 +1,61 @@
package org.openscience.cdk.hash.stereo.parity;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author John May
* @cdk.module test-hash
*/
public class CombinedPermutationParityTest {
@Test
public void testParity() throws Exception {
PermutationParity left = mock(PermutationParity.class);
PermutationParity right = mock(PermutationParity.class);
PermutationParity parity = new CombinedPermutationParity(left, right);
long[] dummy = new long[5];
when(left.parity(dummy)).thenReturn(-1);
when(right.parity(dummy)).thenReturn(-1);
assertThat(parity.parity(dummy), is(1));
verify(left, times(1)).parity(dummy);
verify(right, times(1)).parity(dummy);
when(left.parity(dummy)).thenReturn(-1);
when(right.parity(dummy)).thenReturn(1);
assertThat(parity.parity(dummy), is(-1));
when(left.parity(dummy)).thenReturn(1);
when(right.parity(dummy)).thenReturn(-1);
assertThat(parity.parity(dummy), is(-1));
when(left.parity(dummy)).thenReturn(1);
when(right.parity(dummy)).thenReturn(1);
assertThat(parity.parity(dummy), is(1));
when(left.parity(dummy)).thenReturn(0);
when(right.parity(dummy)).thenReturn(1);
assertThat(parity.parity(dummy), is(0));
when(left.parity(dummy)).thenReturn(1);
when(right.parity(dummy)).thenReturn(0);
assertThat(parity.parity(dummy), is(0));
when(left.parity(dummy)).thenReturn(0);
when(right.parity(dummy)).thenReturn(-1);
assertThat(parity.parity(dummy), is(0));
when(left.parity(dummy)).thenReturn(-1);
when(right.parity(dummy)).thenReturn(0);
assertThat(parity.parity(dummy), is(0));
}
}
Oops, something went wrong.

0 comments on commit 37ca197

Please sign in to comment.