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

Reply via email to