Skip to content

Commit

Permalink
Eliminate commons-math3 dependency by reimplementing the MersenneTwis…
Browse files Browse the repository at this point in the history
…ter logic. This was only used for a over-complicated PRNG used in the shortest path fingerprinter. A simpler linear-feedback shift PRNG would work just as well here but we want to keep backwards compatbility. Note this fingerprint it's really very good since the fingerprints are non-transativie and can't be used for substructure screening.
  • Loading branch information
johnmay authored and egonw committed Jan 4, 2022
1 parent ff49a24 commit d447e9a
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 27 deletions.
5 changes: 0 additions & 5 deletions descriptor/fingerprint/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@
<groupId>javax.vecmath</groupId>
<artifactId>vecmath</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,157 @@
* 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 USA.
*
* Original algorithm from commons-math3.
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openscience.cdk.fingerprint;

import java.io.Serializable;
import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomAdaptor;
import org.apache.commons.math3.random.RandomGenerator;

/**
* Generates pseudorandom numbers using the MersenneTwister method from commons-math.
* Generates pseudorandom numbers using the MersenneTwister adapted from commons-math3.
* Previously we included commons-math3 as a dependency but since this was the only usage
* we have reimplemented the algorithm here.<br/>
*
* Note - since chemical fingerprints don't need to be secure a very simple PRNG
* like XorShift would be simpler and faster:
*
* <pre>{@code
* long xorshift64(long seed) {
* seed = seed ^ seed << 21;
* seed = seed ^ seed >>> 35;
* return seed ^ seed << 4;
* }
* }</pre>
*
* @author Syed Asad Rahman (2012)
* @author John Mayfield
* @cdk.keyword fingerprint
* @cdk.keyword similarity
* @cdk.module fingerprint
* @cdk.githash
*/
public class RandomNumber implements Serializable {

private static final long serialVersionUID = 23345464573453571L;

private transient final RandomGenerator rg = new RandomAdaptor(new MersenneTwister());

/**
* Mersenne Twister Random Number for a hashcode within a range between 0 to n.
*
* @param n the maximum value the
* @param seed the seed for the next pseudorandom number
* @return next pseudorandom number
*/
public int generateMersenneTwisterRandomNumber(int n, long seed) {
rg.setSeed(seed);
return rg.nextInt(n);
final class RandomNumber implements Serializable {

private static final int N = 624;
private static final int M = 397;
private static final int[] MAG01 = new int[]{0, -1727483681};
private int[] mt = new int[624];
private int mti;

public RandomNumber() {
}

private void setSeed(int seed) {
int[] seeds = new int[]{0,seed};
long longMT = 19650218;
this.mt[0] = (int) longMT;

for (this.mti = 1; this.mti < 624; ++this.mti) {
longMT = 1812433253L * (longMT ^ longMT >> 30) + (long) this.mti & 4294967295L;
this.mt[this.mti] = (int) longMT;
}

int i = 1;
int j = 0;

int k;
long l0;
long l1;
long l;
for (k = 624; k != 0; --k) {
l0 = (long) this.mt[i] & 2147483647L | (this.mt[i] < 0 ? 2147483648L : 0L);
l1 = (long) this.mt[i - 1] & 2147483647L | (this.mt[i - 1] < 0 ? 2147483648L : 0L);
l = (l0 ^ (l1 ^ l1 >> 30) * 1664525L) + (long) seeds[j] + (long) j;
this.mt[i] = (int) (l & 4294967295L);
++i;
++j;
if (i >= 624) {
this.mt[0] = this.mt[623];
i = 1;
}

if (j >= 2) {
j = 0;
}
}

for (k = 623; k != 0; --k) {
l0 = (long) this.mt[i] & 2147483647L | (this.mt[i] < 0 ? 2147483648L : 0L);
l1 = (long) this.mt[i - 1] & 2147483647L | (this.mt[i - 1] < 0 ? 2147483648L : 0L);
l = (l0 ^ (l1 ^ l1 >> 30) * 1566083941L) - (long) i;
this.mt[i] = (int) (l & 4294967295L);
++i;
if (i >= 624) {
this.mt[0] = this.mt[623];
i = 1;
}
}

this.mt[0] = -2147483648;
}

private int next(int bits) {
int y;
if (this.mti >= 624) {
int mtNext = this.mt[0];

int k;
int mtCurr;
for (k = 0; k < 227; ++k) {
mtCurr = mtNext;
mtNext = this.mt[k + 1];
y = mtCurr & -2147483648 | mtNext & 2147483647;
this.mt[k] = this.mt[k + 397] ^ y >>> 1 ^ MAG01[y & 1];
}

for (k = 227; k < 623; ++k) {
mtCurr = mtNext;
mtNext = this.mt[k + 1];
y = mtCurr & -2147483648 | mtNext & 2147483647;
this.mt[k] = this.mt[k + -227] ^ y >>> 1 ^ MAG01[y & 1];
}

y = mtNext & -2147483648 | this.mt[0] & 2147483647;
this.mt[623] = this.mt[396] ^ y >>> 1 ^ MAG01[y & 1];
this.mti = 0;
}

y = this.mt[this.mti++];
y ^= y >>> 11;
y ^= y << 7 & -1658038656;
y ^= y << 15 & -272236544;
y ^= y >>> 18;
return y >>> 32 - bits;
}

public int generateMersenneTwisterRandomNumber(int n, int seed) {
setSeed(seed);

if ((n & -n) == n) {
return (int) ((long) n * (long) this.next(31) >> 31);
} else {
int bits;
int val;
do {
bits = this.next(31);
val = bits % n;
} while (bits - val + (n - 1) < 0);

return val;
}
}
}

0 comments on commit d447e9a

Please sign in to comment.