Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private StackFrame() {}
if (builder == null) {
builder = findBuilder();
if (builder == null)
throw new IllegalArgumentException("Please ensure query molecule" +
throw new IllegalArgumentException("Please ensure query molecule " +
"has a IChemObjectBuilder set!");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2022 John Mayfield
*
* Contact: [email protected]
*
* 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.isomorphism;

import java.util.BitSet;
import java.util.function.Predicate;

/**
* A predicate for filtering atom-mapping results. This filter only returns
* exclusive (non-overlapping) matching. Note the order the mappings are added
* can impact if they are accepted or not by the filter: <br/>
*
* <pre>{@code
* [0, 1, 2] => accept
* [0, 1, 3] => reject (overlaps with 0 and 1)
* [2, 1, 4] => reject (overlaps with 1 and 2)
* [5, 2, 6] => reject (overlaps with 2)
* }</pre>
* vs.<br/>
* <pre>{@code
* [0, 1, 3] => accept
* [0, 1, 2] => reject (overlaps with 0 and 1)
* [2, 1, 4] => reject (overlaps with 1 and 2)
* [5, 2, 6] => accept
* }</pre>
*
* This class is intended for use with {@link Pattern}.
*
* <blockquote><pre>{@code
* Pattern pattern = Pattern.findSubstructure(query);
* List<int[]> unique = FluentIterable.of(patter.matchAll(target))
* .filter(new ExclusiveAtomMatches())
* .toList();
* }</pre></blockquote>
*
* @author John Mayfield
* @cdk.module isomorphism
*/
final class ExclusiveAtomMatches implements Predicate<int[]> {

/**
* Which atoms have we seen in a mapping already.
*/
private final BitSet visit = new BitSet();

/**
* Create filter for unique matches.
*/
public ExclusiveAtomMatches() {
}

/**
* {@inheritDoc}
*/
@Override
public boolean test(int[] mapping) {
if (none(mapping))
return add(mapping);
return false;
}

// Has none of the atom indexes been seen already?
boolean none(int[] mapping) {
for (int atomIdx : mapping)
if (this.visit.get(atomIdx))
return false;
return true;
}

boolean add(int[] mapping) {
for (int atomIdx : mapping)
this.visit.set(atomIdx);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,19 +271,58 @@ public Mappings stereochemistry() {
}

/**
* Filter the mappings for those which cover a unique set of atoms in the
* Filter the mappings for those which cover a unique atoms in the
* target. The unique atom mappings are a subset of the unique bond
* matches.
*
* @return fluent-api instance
* @see #uniqueBonds()
* @see #uniqueAtomSets()
* @see #exclusiveAtoms()
*/
public Mappings uniqueAtoms() {
// we need the unique predicate to be reset for each new iterator -
// otherwise multiple iterations are always filtered (seen before)
return new Mappings(query, target, () -> stream().filter(new UniqueAtomMatches()).iterator());
}

/**
* Filter the mappings for those which cover a unique set of atoms in the
* target. The unique atom mappings are a subset of the unique atom set
* matches.
*
* @return fluent-api instance
* @see #uniqueBonds()
* @see #uniqueAtomSets()
* @see #exclusiveAtoms()
*/
public Mappings uniqueAtomSets() {
// we need the unique predicate to be reset for each new iterator -
// otherwise multiple iterations are always filtered (seen before)
return new Mappings(query, target, () -> stream().filter(new UniqueAtomSetMatches()).iterator());
}

/**
* Filter the mappings for those which cover an exclusive set of atoms in
* the target. If a match overlaps with another one it is not returned. For
* example suppose we had the query {@code C~O} and matched against a
* carboxylic acid {@code *C(O)=O}, there are <b>2</b> unique matches but
* only <b>1</b> exclusive match. If we had two -CO2 groups
* ({@code c1ccc(C(O)=O)cc1C(O)=O} there are {@cdoe 4} unique matches and
* {@code 2} exclusive matches.
* The exclusive atom mappings are therefore a subset of the unique atom
* matches.
*
* @return fluent-api instance
* @see #uniqueAtoms()
* @see org.openscience.cdk.isomorphism.ExclusiveAtomMatches
*/
public Mappings exclusiveAtoms() {
// we need the unique predicate to be reset for each new iterator -
// otherwise multiple iterations are always filtered (seen before)
return new Mappings(query, target, () -> stream().filter(new ExclusiveAtomMatches()).iterator());
}

/**
* Filter the mappings for those which cover a unique set of bonds in the
* target.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2013 European Bioinformatics Institute (EMBL-EBI)
* John May <[email protected]>
* 2022 John Mayfield (né May)
*
* Contact: [email protected]
*
Expand All @@ -25,13 +26,18 @@
package org.openscience.cdk.isomorphism;

import java.util.BitSet;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

/**
* A predicate for filtering atom-mapping results. This class is intended for
* use with {@link Pattern}.
* A predicate for filtering atom-mapping results.
*
* <pre>{@code
* [0, 1, 2] => accept
* [0, 1, 3] => accept
* [2, 1, 3] => reject (no new mappings)
* </pre>
*
* This class is intended for use with {@link Pattern}.
*
* <blockquote><pre>{@code
* Pattern pattern = Ullmann.findSubstructure(query);
Expand All @@ -40,59 +46,54 @@
* .toList();
* }</pre></blockquote>
*
* @author John May
* @author John Mayfield
* @cdk.module isomorphism
*/
final class UniqueAtomMatches implements Predicate<int[]> {

/** Which mappings have we seen already. */
private final Set<BitSet> unique;

/**
* Create filter for the expected number of unique matches. The number
* of matches can grow if required.
*
* @param expectedHits expected number of unique matches
* Which atoms have we seen in a mapping already.
*/
private UniqueAtomMatches(int expectedHits) {
this.unique = new HashSet<>(2*expectedHits);
}
private final BitSet visit = new BitSet();

/**
* Create filter for unique matches.
*/
public UniqueAtomMatches() {
this(10);
}

/**
*{@inheritDoc}
* {@inheritDoc}
*/
@Override
public boolean test(int[] input) {
return unique.add(toBitSet(input));
public boolean test(int[] mapping) {
if (some(mapping))
return add(mapping);
return false;
}

// If some (at least one) has not been seen yet
boolean some(int[] mapping) {
for (int atomIdx : mapping)
if (!this.visit.get(atomIdx))
return true;
return false;
}

boolean add(int[] mapping) {
for (int atomIdx : mapping)
this.visit.set(atomIdx);
return true;
}

/**
* Backwards compatible method from when we used GUAVA predicates.
*
* @param ints atom index bijection
* @return true/false
* @see #test(int[])
*/
public boolean apply(int[] ints) {
return test(ints);
}

/**
* Convert a mapping to a bitset.
*
* @param mapping an atom mapping
* @return a bit set of the mapped vertices (values in array)
*/
private BitSet toBitSet(int[] mapping) {
BitSet hits = new BitSet();
for (int v : mapping)
hits.set(v);
return hits;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2022 John Mayfield (né May)
*
* Contact: [email protected]
*
* 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.isomorphism;

import java.util.BitSet;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

/**
* A predicate for filtering atom-mapping results. This implementation differs
* from {@link org.openscience.cdk.isomorphism.UniqueAtomMatches} in that it
* allows unique sets of matches:
*
* <pre>{@code
* [0, 1, 2] => accept
* [0, 1, 3] => accept
* [2, 1, 3] => accept (nothing new but not in this combination)
* </pre>
*
* This class is intended for use with {@link Pattern}.
*
* <blockquote><pre>{@code
* Pattern pattern = Pattern.findSubstructure(query);
* List<int[]> unique = FluentIterable.of(patter.matchAll(target))
* .filter(new UniqueAtomMatches())
* .toList();
* }</pre></blockquote>
*
* @author John Mayfield
* @cdk.module isomorphism
*/
final class UniqueAtomSetMatches implements Predicate<int[]> {

/**
* Which atoms have we seen in a mapping already.
*/
private final Set<BitSet> visit = new HashSet<>();

/**
* Create filter for unique matches.
*/
public UniqueAtomSetMatches() {
}

/**
* {@inheritDoc}
*/
@Override
public boolean test(int[] mapping) {
return add(mapping);
}

private boolean add(int[] mapping) {
return this.visit.add(toBitSet(mapping));
}

/**
* Backwards compatible method from when we used GUAVA predicates.
*
* @param ints atom index bijection
* @return true/false
* @see #test(int[])
*/
public boolean apply(int[] ints) {
return test(ints);
}

// If some (at least one) has not been seen yet
private boolean some(int[] mapping) {
return !visit.contains(toBitSet(mapping));
}

public BitSet toBitSet(int[] mapping) {
BitSet bs = new BitSet();
for (int x : mapping)
bs.set(x);
return bs;
}
}
Loading