graph-dictlearn 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nimendra Gunawardana
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: graph_dictlearn
3
+ Version: 0.1.0
4
+ Summary: A set of representation learning and dictionary learning algorithms for graph data.
5
+ Author-email: Nimendra Gnawardana <nimendraamiththa1@gmail.com>, Siyathma Wedamulla <wedamullasiyathma@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/nimendra-ag/graph_dictlearn
8
+ Project-URL: Repository, https://github.com/nimendra-ag/graph_dictlearn
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: gensim==4.4.0
20
+ Requires-Dist: joblib==1.5.3
21
+ Requires-Dist: matplotlib==3.11.0
22
+ Requires-Dist: networkx==3.6.1
23
+ Requires-Dist: numpy==2.5.0
24
+ Requires-Dist: pandas==3.0.3
25
+ Requires-Dist: rdkit==2026.3.3
26
+ Requires-Dist: scikit-learn==1.9.0
27
+ Requires-Dist: scipy==1.18.0
28
+ Requires-Dist: seaborn==0.13.2
29
+ Dynamic: license-file
30
+
31
+ # graph_dictlearn
32
+ A set of representation learning and dictionary learning algorithms for graph data.
33
+
34
+ You can use this pip package to apply dictionary learning algorithms for graph data.
@@ -0,0 +1,4 @@
1
+ # graph_dictlearn
2
+ A set of representation learning and dictionary learning algorithms for graph data.
3
+
4
+ You can use this pip package to apply dictionary learning algorithms for graph data.
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "graph_dictlearn"
7
+ version = "0.1.0"
8
+ description = "A set of representation learning and dictionary learning algorithms for graph data."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.12"
12
+ authors = [
13
+ {name = "Nimendra Gnawardana", email = "nimendraamiththa1@gmail.com"},
14
+ {name = "Siyathma Wedamulla", email = "wedamullasiyathma@gmail.com"}
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ ]
25
+ dependencies = [
26
+ "gensim==4.4.0",
27
+ "joblib==1.5.3",
28
+ "matplotlib==3.11.0",
29
+ "networkx==3.6.1",
30
+ "numpy==2.5.0",
31
+ "pandas==3.0.3",
32
+ "rdkit==2026.3.3",
33
+ "scikit-learn==1.9.0",
34
+ "scipy==1.18.0",
35
+ "seaborn==0.13.2",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/nimendra-ag/graph_dictlearn"
40
+ Repository = "https://github.com/nimendra-ag/graph_dictlearn"
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """A set of representation learning and dictionary learning algorithms for graph data."""
2
+
3
+ from graph_dictlearn.loader.loader import SDFLoader
4
+ from graph_dictlearn.encoders.wl import WL
5
+ from graph_dictlearn.dict_learners.aksvd import AKSVD
6
+
7
+ __all__ = ["SDFLoader", "WL", "AKSVD"]
8
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ """Dictionary learning implementations."""
2
+
3
+ from graph_dictlearn.dict_learners.aksvd import AKSVD
4
+
5
+ __all__ = ["AKSVD"]
@@ -0,0 +1,30 @@
1
+ from graph_dictlearn.dict_learners.base import DictLearner
2
+ from graph_dictlearn.dict_learners.ksvd import ApproximateKSVD
3
+
4
+
5
+ class AKSVD(DictLearner):
6
+ def __init__(
7
+ self,
8
+ dimensions: int = 16,
9
+ max_iter: int = 10,
10
+ tol: float = 1e-6,
11
+ n_non_zero_coefs: int = 10,
12
+ ):
13
+ super().__init__(name="AKSVD")
14
+ self._dictionary = None
15
+ self.dimensions = dimensions
16
+ self.max_iter = max_iter
17
+ self.tol = tol
18
+ self.n_non_zero_coefs = n_non_zero_coefs
19
+ self.aksvd = ApproximateKSVD(n_components=self.dimensions, max_iter=self.max_iter, tol=self.tol,
20
+ transform_n_nonzero_coefs=self.n_non_zero_coefs)
21
+
22
+ def fit(self, training_graph_embeddings):
23
+ self._dictionary = self.aksvd.fit(training_graph_embeddings).components_
24
+
25
+ # self._embedding = self.aksvd.transform(training_graph_embeddings)
26
+ return self
27
+
28
+ def infer(self, infer_graph_embeddings):
29
+ sparse_embeddings = self.aksvd.transform(infer_graph_embeddings)
30
+ return sparse_embeddings
@@ -0,0 +1,13 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ class DictLearner(ABC):
4
+ def __init__(self, name: str):
5
+ self.name = name
6
+
7
+ @abstractmethod
8
+ def fit(self, training_graph_embeddings):
9
+ pass
10
+
11
+ @abstractmethod # ← add this decorator
12
+ def infer(self, infer_graph_embeddings):
13
+ pass
@@ -0,0 +1,84 @@
1
+ import numpy as np
2
+ import scipy as sp
3
+ from sklearn.linear_model import orthogonal_mp_gram
4
+
5
+
6
+ class ApproximateKSVD():
7
+ def __init__(self, n_components, max_iter=10, tol=1e-6,
8
+ transform_n_nonzero_coefs=None):
9
+ """
10
+ Parameters
11
+ ----------
12
+ n_components:
13
+ Number of dictionary elements
14
+
15
+ max_iter:
16
+ Maximum number of iterations
17
+
18
+ tol:
19
+ tolerance for error
20
+
21
+ transform_n_nonzero_coefs:
22
+ Number of nonzero coefficients to target
23
+ """
24
+ self.components_ = None
25
+ self.max_iter = max_iter
26
+ self.tol = tol
27
+ self.n_components = n_components
28
+ self.transform_n_nonzero_coefs = transform_n_nonzero_coefs
29
+
30
+ def _update_dict(self, X, D, gamma):
31
+ for j in range(self.n_components):
32
+ I = gamma[:, j] > 0
33
+ if np.sum(I) == 0:
34
+ continue
35
+
36
+ D[j, :] = 0
37
+ g = gamma[I, j].T
38
+ r = X[I, :] - gamma[I, :].dot(D)
39
+ d = r.T.dot(g)
40
+ d /= np.linalg.norm(d)
41
+ g = r.dot(d)
42
+ D[j, :] = d
43
+ gamma[I, j] = g.T
44
+ return D, gamma
45
+
46
+ def _initialize(self, X):
47
+ if min(X.shape) < self.n_components:
48
+ D = np.random.randn(self.n_components, X.shape[1])
49
+ else:
50
+ u, s, vt = sp.sparse.linalg.svds(X, k=self.n_components)
51
+ D = np.dot(np.diag(s), vt)
52
+ D /= np.linalg.norm(D, axis=1)[:, np.newaxis]
53
+ return D
54
+
55
+ def _transform(self, D, X):
56
+ gram = D.dot(D.T)
57
+ Xy = D.dot(X.T)
58
+
59
+ n_nonzero_coefs = self.transform_n_nonzero_coefs
60
+ if n_nonzero_coefs is None:
61
+ n_nonzero_coefs = int(0.1 * X.shape[1])
62
+
63
+ return orthogonal_mp_gram(
64
+ gram, Xy, n_nonzero_coefs=n_nonzero_coefs).T
65
+
66
+ def fit(self, X):
67
+ """
68
+ Parameters
69
+ ----------
70
+ X: shape = [n_samples, n_features]
71
+ """
72
+ D = self._initialize(X)
73
+ for i in range(self.max_iter):
74
+ gamma = self._transform(D, X)
75
+ e = np.linalg.norm(X - gamma.dot(D))
76
+ if e < self.tol:
77
+ break
78
+ D, gamma = self._update_dict(X, D, gamma)
79
+
80
+ self.components_ = D
81
+ return self
82
+
83
+ def transform(self, X):
84
+ return self._transform(self.components_, X)
@@ -0,0 +1,5 @@
1
+ """Graph encoder implementations."""
2
+
3
+ from graph_dictlearn.encoders.wl import WL
4
+
5
+ __all__ = ["WL"]
@@ -0,0 +1,54 @@
1
+ from abc import ABC, abstractmethod
2
+ import random
3
+ import numpy as np
4
+ import networkx as nx
5
+ from typing import List
6
+
7
+ # abstract base class
8
+ class GraphEncoder(ABC):
9
+ def __init__(self, name: str):
10
+ super().__init__()
11
+ self.name = name
12
+ self.embeddings = None
13
+
14
+ @abstractmethod
15
+ def generate_training_embeddings(self, graphs):
16
+ pass
17
+
18
+ @abstractmethod
19
+ def generate_inferencing_embeddings(self, graphs):
20
+ pass
21
+
22
+ def _set_seed(self):
23
+ """Creating the initial random seed."""
24
+ random.seed(self.seed)
25
+ np.random.seed(self.seed)
26
+
27
+ @staticmethod
28
+ def _ensure_integrity(graph: nx.classes.graph.Graph) -> nx.classes.graph.Graph:
29
+ """Ensure walk traversal conditions."""
30
+ edge_list = [(index, index) for index in range(graph.number_of_nodes())]
31
+ graph.add_edges_from(edge_list)
32
+
33
+ return graph
34
+
35
+ @staticmethod
36
+ def _check_indexing(graph: nx.classes.graph.Graph):
37
+ """Checking the consecutive numeric indexing."""
38
+ numeric_indices = [index for index in range(graph.number_of_nodes())]
39
+ node_indices = sorted([node for node in graph.nodes()])
40
+
41
+ assert numeric_indices == node_indices, "The node indexing is wrong."
42
+
43
+ def _check_graph(self, graph: nx.classes.graph.Graph) -> nx.classes.graph.Graph:
44
+ """Check the Karate Club assumptions about the graph."""
45
+ self._check_indexing(graph)
46
+ graph = self._ensure_integrity(graph)
47
+
48
+ return graph
49
+
50
+ def _check_graphs(self, graphs: List[nx.classes.graph.Graph]):
51
+ """Check the Karate Club assumptions for a list of graphs."""
52
+ graphs = [self._check_graph(graph) for graph in graphs]
53
+
54
+ return graphs
@@ -0,0 +1,147 @@
1
+ import numpy as np
2
+ from gensim.models.doc2vec import TaggedDocument
3
+ from graph_dictlearn.encoders.base import GraphEncoder
4
+ from graph_dictlearn.encoders.wl_hashing import WeisfeilerLehmanHashing
5
+ from collections import Counter
6
+
7
+ import logging
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class WL(GraphEncoder):
12
+ def __init__(
13
+ self,
14
+ wl_iterations: int = 2,
15
+ attributed: bool = True,
16
+ erase_base_features: bool = True,
17
+ n_vocab: int = 1000,
18
+ min_features: int = 50,
19
+ seed: int = 42
20
+ ):
21
+
22
+ super().__init__(name="ImbalanceAwareWL")
23
+
24
+ self.seed = seed
25
+ self.vocab = None
26
+ self.graph_embeddings = None
27
+ self.wl_iterations = wl_iterations
28
+ self.attributed = attributed
29
+ self.erase_base_features = erase_base_features
30
+ self.n_vocab = n_vocab
31
+ self.min_features = min_features
32
+
33
+ def create_wl_hash(self, graph_list):
34
+
35
+ documents = []
36
+
37
+ for graph in graph_list:
38
+ g = self._check_graph(graph)
39
+
40
+ document = WeisfeilerLehmanHashing(
41
+ g, self.wl_iterations, self.attributed, self.erase_base_features)
42
+
43
+ documents.append(document)
44
+
45
+ documents = [
46
+ TaggedDocument(words=doc.get_graph_features(), tags=[str(i)])
47
+ for i, doc in enumerate(documents)
48
+ ]
49
+
50
+ return documents
51
+
52
+ def create_vocab(self, corpus, labels):
53
+ majority_df = Counter()
54
+ minority_df = Counter()
55
+
56
+ majority_graphs = 0
57
+ minority_graphs = 0
58
+
59
+ for doc, label in zip(corpus, labels):
60
+
61
+ # unique subtree hashes in this graph
62
+ # document frequency instead of raw counts
63
+ unique_words = Counter(doc.words)
64
+ if label == -1:
65
+ majority_graphs += 1
66
+ for word in unique_words:
67
+ majority_df[word] += 1
68
+ else:
69
+ minority_graphs += 1
70
+ for word in unique_words:
71
+ minority_df[word] += 1
72
+
73
+ all_words = set(list(majority_df.keys()) + list(minority_df.keys()))
74
+
75
+ scored_vocab = []
76
+
77
+ for word in all_words:
78
+ p_majority = majority_df[word] / majority_graphs
79
+
80
+ p_minority = (minority_df[word] / minority_graphs)
81
+
82
+ discriminative_score = abs(np.sqrt(p_majority) - np.sqrt(p_minority))
83
+
84
+ total_presence = p_majority + p_minority
85
+
86
+ # Final score
87
+
88
+ score = total_presence * discriminative_score
89
+ scored_vocab.append((word, score))
90
+
91
+ # Sort features by discriminative importance
92
+ scored_vocab = sorted(
93
+ scored_vocab,
94
+ key=lambda x: x[1],
95
+ reverse=True
96
+ )
97
+
98
+ # selection
99
+ scores = np.array([x[1] for x in scored_vocab])
100
+
101
+ threshold = scores.mean() - scores.std()
102
+ trimmed_vocab = [item for item in scored_vocab if item[1] >= threshold]
103
+
104
+ # fallback if too few selected
105
+ logger.info("Selected %d features from adaptive selection", len(trimmed_vocab))
106
+ if len(trimmed_vocab) < 50:
107
+ trimmed_vocab = scored_vocab[:self.n_vocab]
108
+
109
+ self.n_vocab = len(trimmed_vocab)
110
+ return trimmed_vocab
111
+
112
+ def calc_coefficients(self, corpus):
113
+
114
+ sparse_vector = np.zeros([len(corpus), self.n_vocab])
115
+
116
+ i = 0
117
+ for corpus in corpus:
118
+ words = corpus.words
119
+
120
+ words_count = Counter(corpus.words)
121
+ j = 0
122
+ for atom, _ in self.vocab:
123
+ sparse_vector[i][j] = words_count[atom]
124
+ j = j + 1
125
+
126
+ i = i + 1
127
+
128
+ norms = np.linalg.norm(sparse_vector, axis=1, keepdims=True)
129
+ norms[norms == 0] = 1.0
130
+ sparse_vector = sparse_vector / norms
131
+
132
+ return sparse_vector
133
+
134
+ def generate_training_embeddings(self, graphs, labels):
135
+ self._set_seed()
136
+ documents = self.create_wl_hash(graphs)
137
+ self.vocab = self.create_vocab(documents, labels)
138
+ train_graph_embeddings = self.calc_coefficients(documents)
139
+ return train_graph_embeddings
140
+
141
+ def generate_inferencing_embeddings(self, graphs):
142
+ self._set_seed()
143
+ documents = self.create_wl_hash(graphs)
144
+ infer_graph_embeddings = self.calc_coefficients(
145
+ documents
146
+ )
147
+ return infer_graph_embeddings
@@ -0,0 +1,97 @@
1
+ import hashlib
2
+ import networkx as nx
3
+ from typing import List, Dict
4
+
5
+
6
+ class WeisfeilerLehmanHashing(object):
7
+ """
8
+ Weisfeiler-Lehman feature extractor class.
9
+
10
+ Args:
11
+ graph (NetworkX graph): NetworkX graph for which we do WL hashing.
12
+ wl_iterations (int): Number of WL iterations.
13
+ attributed (bool): Presence of attributes.
14
+ erase_base_feature (bool): Deleting the base features.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ graph: nx.classes.graph.Graph,
20
+ wl_iterations: int,
21
+ attributed: bool,
22
+ erase_base_features: bool,
23
+ ):
24
+ """
25
+ Initialization method which also executes feature extraction.
26
+ """
27
+ self.wl_iterations = wl_iterations
28
+ self.graph = graph
29
+ self.attributed = attributed
30
+ self.erase_base_features = erase_base_features
31
+ self._set_features()
32
+ self._do_recursions()
33
+
34
+ def _set_features(self):
35
+ """
36
+ Creating the features.
37
+ """
38
+ if self.attributed:
39
+ self.features = nx.get_node_attributes(self.graph, "feature")
40
+ else:
41
+ self.features = {
42
+ node: self.graph.degree(node) for node in self.graph.nodes()
43
+ }
44
+ self.extracted_features = {k: [str(v)] for k, v in self.features.items()}
45
+
46
+ def _erase_base_features(self):
47
+ """
48
+ Erasing the base features
49
+ """
50
+ for k, v in self.extracted_features.items():
51
+ del self.extracted_features[k][0]
52
+
53
+ def _do_a_recursion(self):
54
+ """
55
+ The method does a single WL recursion.
56
+
57
+ Return types:
58
+ * **new_features** *(dict of strings)* - The hash table with extracted WL features.
59
+ """
60
+ new_features = {}
61
+ for node in self.graph.nodes():
62
+ nebs = self.graph.neighbors(node)
63
+ degs = [self.features[neb] for neb in nebs]
64
+ features = [str(self.features[node])] + sorted([str(deg) for deg in degs])
65
+ features = "_".join(features)
66
+ hash_object = hashlib.md5(features.encode())
67
+ hashing = hash_object.hexdigest()
68
+ new_features[node] = hashing
69
+ self.extracted_features = {
70
+ k: self.extracted_features[k] + [v] for k, v in new_features.items()
71
+ }
72
+ return new_features
73
+
74
+ def _do_recursions(self):
75
+ """
76
+ The method does a series of WL recursions.
77
+ """
78
+ for _ in range(self.wl_iterations):
79
+ self.features = self._do_a_recursion()
80
+ if self.erase_base_features:
81
+ self._erase_base_features()
82
+
83
+ def get_node_features(self) -> Dict[int, List[str]]:
84
+ """
85
+ Return the node level features.
86
+ """
87
+ return self.extracted_features
88
+
89
+ def get_graph_features(self) -> List[str]:
90
+ """
91
+ Return the graph level features.
92
+ """
93
+ return [
94
+ feature
95
+ for node, features in self.extracted_features.items()
96
+ for feature in features
97
+ ]
@@ -0,0 +1,5 @@
1
+ """Graph data loaders."""
2
+
3
+ from graph_dictlearn.loader.loader import SDFLoader
4
+
5
+ __all__ = ["SDFLoader"]
@@ -0,0 +1,86 @@
1
+ """Generic graph data loader for SDF molecular files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from pathlib import Path
7
+ from typing import List, Tuple
8
+
9
+ import networkx as nx
10
+ from rdkit import Chem
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class SDFLoader:
16
+ """Load molecular graphs and labels from an SDF file.
17
+
18
+ Parameters
19
+ ----------
20
+ filepath : str or Path
21
+ Path to the .sdf file.
22
+ label_property : str
23
+ SDF molecule property that holds the class label. Defaults to "value".
24
+ remove_hydrogens : bool
25
+ Whether to strip explicit hydrogens. Defaults to False.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ filepath: str | Path,
31
+ label_property: str = "value",
32
+ remove_hydrogens: bool = False,
33
+ ) -> None:
34
+ self.filepath = Path(filepath)
35
+ self.label_property = label_property
36
+ self.remove_hydrogens = remove_hydrogens
37
+
38
+ if not self.filepath.exists():
39
+ raise FileNotFoundError(f"SDF file not found: {self.filepath}")
40
+ if self.filepath.suffix.lower() != ".sdf":
41
+ raise ValueError(f"Expected an .sdf file, got '{self.filepath.suffix}'")
42
+
43
+ def load(self) -> Tuple[List[nx.Graph], List[int]]:
44
+ """Read the SDF file and return (graphs, labels)."""
45
+ supplier = Chem.SDMolSupplier(
46
+ str(self.filepath),
47
+ removeHs=self.remove_hydrogens,
48
+ )
49
+
50
+ graphs: List[nx.Graph] = []
51
+ labels: List[int] = []
52
+
53
+ for mol in supplier:
54
+ if mol is None:
55
+ continue
56
+
57
+ graph = self._mol_to_graph(mol)
58
+ label = int(float(mol.GetProp(self.label_property)))
59
+
60
+ graphs.append(graph)
61
+ labels.append(label)
62
+
63
+ logger.info("Loaded %d graphs from %s", len(graphs), self.filepath.name)
64
+ return graphs, labels
65
+
66
+ @staticmethod
67
+ def _mol_to_graph(mol) -> nx.Graph:
68
+ """Convert an RDKit Mol to a NetworkX graph."""
69
+ graph = nx.Graph()
70
+
71
+ for atom in mol.GetAtoms():
72
+ graph.add_node(atom.GetIdx(), feature=atom.GetSymbol())
73
+
74
+ for bond in mol.GetBonds():
75
+ graph.add_edge(
76
+ bond.GetBeginAtomIdx(),
77
+ bond.GetEndAtomIdx(),
78
+ bond_type=str(bond.GetBondType()),
79
+ bond_order=bond.GetBondTypeAsDouble(),
80
+ aromatic=bond.GetIsAromatic(),
81
+ in_ring=bond.IsInRing(),
82
+ conjugated=bond.GetIsConjugated(),
83
+ stereo=str(bond.GetStereo()),
84
+ )
85
+
86
+ return graph
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: graph_dictlearn
3
+ Version: 0.1.0
4
+ Summary: A set of representation learning and dictionary learning algorithms for graph data.
5
+ Author-email: Nimendra Gnawardana <nimendraamiththa1@gmail.com>, Siyathma Wedamulla <wedamullasiyathma@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/nimendra-ag/graph_dictlearn
8
+ Project-URL: Repository, https://github.com/nimendra-ag/graph_dictlearn
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: gensim==4.4.0
20
+ Requires-Dist: joblib==1.5.3
21
+ Requires-Dist: matplotlib==3.11.0
22
+ Requires-Dist: networkx==3.6.1
23
+ Requires-Dist: numpy==2.5.0
24
+ Requires-Dist: pandas==3.0.3
25
+ Requires-Dist: rdkit==2026.3.3
26
+ Requires-Dist: scikit-learn==1.9.0
27
+ Requires-Dist: scipy==1.18.0
28
+ Requires-Dist: seaborn==0.13.2
29
+ Dynamic: license-file
30
+
31
+ # graph_dictlearn
32
+ A set of representation learning and dictionary learning algorithms for graph data.
33
+
34
+ You can use this pip package to apply dictionary learning algorithms for graph data.
@@ -0,0 +1,19 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/graph_dictlearn/__init__.py
5
+ src/graph_dictlearn.egg-info/PKG-INFO
6
+ src/graph_dictlearn.egg-info/SOURCES.txt
7
+ src/graph_dictlearn.egg-info/dependency_links.txt
8
+ src/graph_dictlearn.egg-info/requires.txt
9
+ src/graph_dictlearn.egg-info/top_level.txt
10
+ src/graph_dictlearn/dict_learners/__init__.py
11
+ src/graph_dictlearn/dict_learners/aksvd.py
12
+ src/graph_dictlearn/dict_learners/base.py
13
+ src/graph_dictlearn/dict_learners/ksvd.py
14
+ src/graph_dictlearn/encoders/__init__.py
15
+ src/graph_dictlearn/encoders/base.py
16
+ src/graph_dictlearn/encoders/wl.py
17
+ src/graph_dictlearn/encoders/wl_hashing.py
18
+ src/graph_dictlearn/loader/__init__.py
19
+ src/graph_dictlearn/loader/loader.py
@@ -0,0 +1,10 @@
1
+ gensim==4.4.0
2
+ joblib==1.5.3
3
+ matplotlib==3.11.0
4
+ networkx==3.6.1
5
+ numpy==2.5.0
6
+ pandas==3.0.3
7
+ rdkit==2026.3.3
8
+ scikit-learn==1.9.0
9
+ scipy==1.18.0
10
+ seaborn==0.13.2
@@ -0,0 +1 @@
1
+ graph_dictlearn