Cheers Andrew, Have made a (hopefully) reasonable patch - https://github.com/cdk/cdk/pull/1305.
On Fri, 25 Sept 2026 at 16:15, John Mayfield <[email protected]> wrote: > I think it should be deduplication identical paths 4_C_O vs 4_O_C are the > same but both get captured but then also double counted! > > If you do that you get the following for your molecule. > > hash: 236 2 4_C_O > hash: 156 6 3_C_C > hash: 78 12 2_C_C > hash: 0 12 1_C_C > hash: 80 4 2_C_O > hash: 2 2 1_C_O > hash: 158 4 3_C_O > > The hash being a bit id also isn't ideal. > > Anyways it's broken in multiple ways, is an atom pair fingerprint that > important? I'm wonder if we just deprecate/delete it. > > Best, > John > > On Fri, 25 Sept 2026 at 16:10, John Mayfield <[email protected]> > wrote: > >> Just had a quick look, yeah it's (very) broken :-). >> >> The issue is there is a "fixed" set of features that it wants to capture >> for binary but count mode gets *all* the features. >> >> So the fix is either the count fingerprint should only report the >> "selected features" or it should hash the path directly. I'll have a bit >> more a think but and may need to check the original PaDEL implementation. >> >> Best, >> John >> >> On Wed, 23 Sept 2026 at 10:25, Andrew Dalke <[email protected]> >> wrote: >> >>> Hi everyone, >>> >>> I'm confused about how to get the count information from the >>> AtomPairs2D fingerprint in CDK and wondered if anyone here could help. >>> >>> Once I get the fingerprint, how do I get the feature index and >>> associated count? >>> >>> When I try it out with my test case, below, I find there are 11 >>> populated bins but I can only pull out 7 hashes (the other indices raise an >>> exception), while I can get all 11 counts. >>> >>> I don't know if this is an error in the CDK implementation, or I simply >>> don't know how to get the data. >>> >>> Assuming the code is working correctly, my two ideas for how to map hash >>> to count are: >>> >>> 1) use fp.getHash(i), fp.getCount(i) when fp.getHash(i) does not throw >>> an exception >>> >>> (I think this is the correct one as fp.getCount(i) matches >>> fp.getCountForHash(fp.getHash(i))) >>> >>> 2) use fp.getHash(i), fp.getCount(j) where j is the j^th fp.getHash(i) >>> which does not throw an exception >>> >>> >>> Here is my reproducible, which is in Python accessing cdk-2.13.jar via >>> the JPype Java/Python bridge, which I set up in my chemfp package for >>> working with cheminformatics fingerprints. >>> >>> ==== >>> # This does the work to initialize the Java/Python bridge >>> from chemfp import cdk_types >>> >>> from org.openscience import cdk >>> from org.openscience.cdk.fingerprint import AtomPairs2DFingerprinter >>> >>> fingerprinter = AtomPairs2DFingerprinter() >>> # prints: >>> # fingerprinter.getSize()=780 >>> print(f"{fingerprinter.getSize()=}") >>> >>> from org.openscience.cdk.smiles import SmilesParser >>> parser = SmilesParser(cdk.silent.SilentChemObjectBuilder.getInstance()) >>> mol = parser.parseSmiles("c1ccccc1O") >>> >>> fp = fingerprinter.getCountFingerprint(mol) >>> # prints: >>> # fp.size()=780 >>> print(f"{fp.size()=}") >>> >>> # prints: >>> # fp.numOfPopulatedbins()=11 >>> print(f"{fp.numOfPopulatedbins()=}") >>> >>> # I expected to get the feature indices as getHash(i) for 0 <= i < 11, >>> # but if I do that manually I get failures for getHash(2) and a few >>> others >>> >>> # >>> fp.getHash(0) >>> # 236 >>> # >>> fp.getHash(1) >>> # 156 >>> # >>> fp.getHash(2) >>> # Traceback (most recent call last): >>> # File "AtomPairs2DFingerprinter.java", line 216, in >>> org.openscience.cdk.fingerprint.AtomPairs2DFingerprinter$1.getHash >>> # Exception: Java Exception >>> # >>> # The above exception was the direct cause of the following exception: >>> # >>> # Traceback (most recent call last): >>> # File "<stdin>", line 1, in <module> >>> # java.lang.java.lang.NullPointerException: >>> java.lang.NullPointerException: Cannot invoke >>> "java.lang.Integer.intValue()" because the return value of >>> "java.util.Map.get(Object)" is null >>> >>> # Instead, I'll scan all of the indices 0 <= i < 780 and get >>> # the sparse index of (i, value) for both the hashes and counts >>> >>> def get_hashes_and_counts(fp): >>> hashes = [] >>> counts = [] >>> for i in range(780): >>> try: >>> hashes.append((i, fp.getHash(i))) >>> except Exception: >>> pass >>> try: >>> counts.append((i, fp.getCount(i))) >>> except Exception: >>> pass >>> >>> return hashes, counts >>> >>> hashes, counts = get_hashes_and_counts(fp) >>> >>> # prints: >>> # hashes=[(0, 236), (1, 156), (3, 78), (4, 0), (5, 80), >>> # (7, 2), (10, 158)] >>> print(f"{hashes=}") >>> >>> # prints: >>> # counts=[(0, 1), (1, 6), (2, 1), (3, 12), (4, 12), (5, 2), >>> # (6, 2), (7, 1), (8, 1), (9, 2), (10, 2)] >>> print(f"{counts=}") >>> >>> # I can see three interpretations: >>> >>> ## == Option 1 == >>> # use (fp.GetHash(i), fp.GetCount(i) if GetHash(i) is valid >>> >>> def get_sparse_features1(fp): >>> features = [] >>> for i in range(fp.numOfPopulatedbins()): >>> try: >>> feature_idx = fp.getHash(i) >>> except Exception: >>> continue >>> # Or use fp.getCountForHash(feature_idx) >>> features.append((feature_idx, fp.getCount(i))) >>> return features >>> >>> # prints: >>> # get_sparse_features1(fp)=[(236, 1), (156, 6), (78, 12), (0, 12), >>> # (80, 2), (2, 1), (158, 2)] >>> print(f"{get_sparse_features1(fp)=}") >>> >>> ## == Option 2 == >>> # use (fp.GetHash(i), fp.GetCount(j) for the j-th valid GetHash(i) >>> >>> def get_sparse_features2(fp): >>> features = [] >>> j = 0 >>> for i in range(fp.numOfPopulatedbins()): >>> try: >>> feature_idx = fp.getHash(i) >>> except Exception: >>> continue >>> features.append((feature_idx, fp.getCount(j))) >>> j += 1 >>> return features >>> >>> # prints: >>> # get_sparse_features2(fp)=[(236, 1), (156, 6), (78, 1), (0, 12), >>> # (80, 12), (2, 2), (158, 2)] >>> print(f"{get_sparse_features2(fp)=}") >>> >>> # (These give different counts than option 1, because of the >>> # different way of using getCount) >>> >>> ## == Option 3 == >>> # Something is wrong with the implementation >>> >>> def get_sparse_features3(fp): >>> raise NotImplementedError("It is not possible to get the count >>> features") >>> >>> >>> Andrew >>> [email protected] >>> >>> >>> >>> >>> >>> _______________________________________________ >>> Cdk-user mailing list >>> [email protected] >>> https://lists.sourceforge.net/lists/listinfo/cdk-user >>> >>
_______________________________________________ Cdk-user mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/cdk-user

