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

