├── tests ├── __init__.py ├── data │ ├── answers │ │ ├── ShortestPath │ │ │ ├── random_10_node.csv │ │ │ ├── random_100_node.csv │ │ │ ├── 8_4x4_ams.csv │ │ │ └── 8_100x100_ams.csv │ │ └── ShortestPathLabeled │ │ │ ├── random_10_node.csv │ │ │ ├── random_100_node.csv │ │ │ ├── 8_4x4_ams.csv │ │ │ └── 8_100x100_ams.csv │ ├── random_10_node_labels.csv │ ├── 8_4x4_ams_labels.csv │ ├── random_10_node.csv │ ├── random_100_node_labels.csv │ ├── 8_4x4_ams.csv │ ├── 8_100x100_ams_labels.csv │ └── random_100_node.csv ├── graphs.py ├── datasets.py ├── shortestpath.py ├── basic.py ├── psd.py ├── graphutils.py └── graphlets.py ├── doc └── img │ └── logo.png ├── pykernels ├── graph │ ├── __init__.py │ ├── data │ │ ├── 3graphlets.csv │ │ └── 4graphlets.csv │ ├── basic.py │ ├── randomwalk.py │ ├── shortestpath.py │ └── allgraphlets.py ├── utils.py ├── __init__.py ├── basic.py ├── base.py └── regular.py ├── example.py ├── .gitignore ├── LICENSE ├── setup.py └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/answers/ShortestPath/random_10_node.csv: -------------------------------------------------------------------------------- 1 | 1117 -------------------------------------------------------------------------------- /tests/data/random_10_node_labels.csv: -------------------------------------------------------------------------------- 1 | 2,2,3,4,5,3,5,1,1,1 -------------------------------------------------------------------------------- /tests/data/answers/ShortestPath/random_100_node.csv: -------------------------------------------------------------------------------- 1 | 12270498 -------------------------------------------------------------------------------- /tests/data/answers/ShortestPathLabeled/random_10_node.csv: -------------------------------------------------------------------------------- 1 | 131 -------------------------------------------------------------------------------- /tests/data/answers/ShortestPathLabeled/random_100_node.csv: -------------------------------------------------------------------------------- 1 | 705666 -------------------------------------------------------------------------------- /doc/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmum/pykernels/HEAD/doc/img/logo.png -------------------------------------------------------------------------------- /tests/data/8_4x4_ams_labels.csv: -------------------------------------------------------------------------------- 1 | 4,4,4,2,4,2,1,3,4,3,2,1,1,4,3,4,3,4,2,2,3,3,2,3,3,1,1,4,4,1,4,1 -------------------------------------------------------------------------------- /pykernels/graph/__init__.py: -------------------------------------------------------------------------------- 1 | from randomwalk import RandomWalk 2 | from allgraphlets import All34Graphlets 3 | from shortestpath import ShortestPath -------------------------------------------------------------------------------- /pykernels/graph/data/3graphlets.csv: -------------------------------------------------------------------------------- 1 | 1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 -------------------------------------------------------------------------------- /tests/data/answers/ShortestPathLabeled/8_4x4_ams.csv: -------------------------------------------------------------------------------- 1 | 20,5,6,8,7,1,3,8,5,10,7,8,5,6,8,6,6,7,10,5,6,6,6,4,8,8,5,14,3,3,10,11,7,5,6,3,8,7,2,2,1,6,6,3,7,15,3,0,3,8,6,10,2,3,14,8,8,6,4,11,2,0,8,13 -------------------------------------------------------------------------------- /tests/data/random_10_node.csv: -------------------------------------------------------------------------------- 1 | 1,1,1,0,1,0,1,0,1,1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,0,1,1,0,1,0,1,0,0,0,1,1,1,1,0,1,1,1,0,0,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,0,0,1 -------------------------------------------------------------------------------- /tests/data/answers/ShortestPath/8_4x4_ams.csv: -------------------------------------------------------------------------------- 1 | 36,38,36,38,24,26,38,28,38,42,38,42,26,27,42,31,36,38,36,38,24,26,38,28,38,42,38,42,26,27,42,31,24,26,24,26,20,20,26,22,26,27,26,27,20,21,27,22,38,42,38,42,26,27,42,31,28,31,28,31,22,22,31,25 -------------------------------------------------------------------------------- /tests/data/random_100_node_labels.csv: -------------------------------------------------------------------------------- 1 | 4,4,3,4,4,5,4,3,5,5,3,2,1,4,1,4,4,6,2,3,5,3,3,1,6,6,2,2,5,1,4,4,6,5,2,4,2,4,4,4,6,4,5,6,6,5,4,6,2,2,4,1,1,5,2,2,4,4,3,1,5,6,3,5,6,6,4,4,5,3,6,6,5,5,6,4,3,1,3,6,6,1,6,5,2,5,1,5,6,5,6,6,1,1,3,5,1,3,4,1 -------------------------------------------------------------------------------- /tests/data/8_4x4_ams.csv: -------------------------------------------------------------------------------- 1 | 1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,1 -------------------------------------------------------------------------------- /tests/graphs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Access layer for datasets used for testing graph kernels 3 | """ 4 | 5 | __author__ = 'kasiajanocha' 6 | 7 | import numpy as np 8 | 9 | def create_random_undirected_adjacency_list(size): 10 | res = np.random.random_integers(0, 1, (size, size)) 11 | res = (res + res.T) % 2 12 | np.fill_diagonal(res, 1) 13 | return res -------------------------------------------------------------------------------- /pykernels/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module with various utility functions 3 | """ 4 | 5 | import numpy as np 6 | 7 | def euclidean_dist_matrix(data_1, data_2): 8 | """ 9 | Returns matrix of pairwise, squared Euclidean distances 10 | """ 11 | norms_1 = (data_1 ** 2).sum(axis=1) 12 | norms_2 = (data_2 ** 2).sum(axis=1) 13 | return np.abs(norms_1.reshape(-1, 1) + norms_2 - 2 * np.dot(data_1, data_2.T)) -------------------------------------------------------------------------------- /tests/datasets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Access layer for datasets used in tests 3 | """ 4 | 5 | __author__ = 'lejlot' 6 | 7 | import numpy as np 8 | 9 | def baseline_logic(operator): 10 | """ Creates 4-point dataset with given logical operator """ 11 | 12 | data = np.array([[1, 1], [0, 0], [1, 0], [0, 1]]) 13 | labels = np.array([max(0, min(1, operator(*point))) for point in data]) 14 | return data, labels 15 | -------------------------------------------------------------------------------- /pykernels/__init__.py: -------------------------------------------------------------------------------- 1 | from basic import Linear, Polynomial, RBF 2 | from regular import Exponential, Laplacian, RationalQuadratic, InverseMultiquadratic, Cauchy, TStudent,\ 3 | ANOVA, Spline, Min, Log, Power, Chi2, AdditiveChi2, GeneralizedHistogramIntersection,\ 4 | Tanimoto, Sorensen, MinMax, Wavelet, Fourier 5 | from graph.randomwalk import RandomWalk 6 | from graph.allgraphlets import All34Graphlets 7 | -------------------------------------------------------------------------------- /tests/data/answers/ShortestPathLabeled/8_100x100_ams.csv: -------------------------------------------------------------------------------- 1 | 704098,644479,657405,657213,627710,662961,637635,617785,644479,719570,638372,580497,616358,593411,614860,628896,657405,638372,775376,656803,575312,627286,624097,526073,657213,580497,656803,714558,635399,650080,647365,607099,627710,616358,575312,635399,667502,608625,645145,669089,662961,593411,627286,650080,608625,699616,621438,613539,637635,614860,624097,647365,645145,621438,644816,632371,617785,628896,526073,607099,669089,613539,632371,705402 -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | from sklearn.svm import SVC 2 | from sklearn.metrics import accuracy_score 3 | import numpy as np 4 | 5 | from pykernels.basic import RBF 6 | 7 | X = np.array([[1,1], [0,0], [1,0], [0,1]]) 8 | y = np.array([1, 1, 0, 0]) 9 | 10 | print 'Testing XOR' 11 | 12 | for clf, name in [(SVC(kernel=RBF(), C=1000), 'pykernel'), (SVC(kernel='rbf', C=1000), 'sklearn')]: 13 | clf.fit(X, y) 14 | print name 15 | print clf 16 | print 'Predictions:', clf.predict(X) 17 | print 'Accuracy:', accuracy_score(clf.predict(X), y) 18 | print 19 | -------------------------------------------------------------------------------- /tests/data/answers/ShortestPath/8_100x100_ams.csv: -------------------------------------------------------------------------------- 1 | 12265482,12258950,12257754,12258766,12262814,12255546,12262446,12260974,12258950,12262500,12263150,12262600,12260400,12264350,12260600,12261400,12257754,12263150,12264138,12263302,12259958,12265962,12260262,12261478,12258766,12262600,12263302,12262708,12260332,12264598,12260548,12261412,12262814,12260400,12259958,12260332,12261828,12259142,12261692,12261148,12255546,12264350,12265962,12264598,12259142,12268938,12259638,12261622,12262446,12260600,12260262,12260548,12261692,12259638,12261588,12261172,12260974,12261400,12261478,12261412,12261148,12261622,12261172,12261268 -------------------------------------------------------------------------------- /pykernels/graph/data/4graphlets.csv: -------------------------------------------------------------------------------- 1 | 1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,1.0,0.0,0.0,1.0,1.0,0.0,1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,1.0,1.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.0,1.0,1.0,1.0,0.0,0.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #####=== Python ===##### 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | # PyBuilder 60 | target/ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 GMUM 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. 22 | 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import setuptools 4 | 5 | LICENSE = 'MIT' 6 | 7 | if __name__ == "__main__": 8 | setuptools.setup( 9 | name='pykernels', 10 | version='0.0.4', 11 | 12 | description='Python library for working with kernel methods in machine learning', 13 | author='Wojciech Marian Czarnecki and Katarzyna Janocha', 14 | author_email='wojciech.czarnecki@uj.edu.pl', 15 | url='https://github.com/gmum/pykernels', 16 | 17 | license=LICENSE, 18 | packages=setuptools.find_packages(), 19 | 20 | install_requires=[ 21 | 'numpy', 22 | 'scipy', 23 | 'scikit-learn', 24 | ], 25 | 26 | classifiers=[ 27 | 'Development Status :: 1 - Alpha', 28 | 'Intended Audience :: Machine Learning Research', 29 | 'License :: OSI Approved ::' + LICENSE, 30 | 'Programming Language :: Python :: 2.7', 31 | 'Programming Language :: Python :: 3', 32 | 'Operating System :: OS Independent', 33 | 'Topic :: Software Development :: Libraries :: Python Modules', 34 | 'Topic :: Scientific/Engineering :: Machine Learning', 35 | 'Topic :: Scientific/Engineering :: Information Analysis', 36 | ], 37 | 38 | zip_safe=True, 39 | include_package_data=True, 40 | ) 41 | -------------------------------------------------------------------------------- /tests/data/8_100x100_ams_labels.csv: -------------------------------------------------------------------------------- 1 | 6,5,3,2,2,6,5,1,5,3,5,2,1,1,1,1,6,6,2,6,5,4,6,1,5,4,4,3,3,3,3,2,1,6,6,3,3,4,1,3,1,2,5,3,3,1,6,1,2,5,4,1,1,5,3,6,2,6,6,6,3,6,3,6,4,3,5,1,6,4,6,5,3,2,2,2,6,2,1,3,1,6,1,5,5,1,4,1,6,3,4,4,2,2,3,5,3,3,6,1,3,2,1,5,2,4,3,3,2,1,3,5,2,5,3,4,3,4,3,2,5,6,3,2,2,3,3,1,3,6,6,1,6,2,6,3,5,4,1,6,2,3,4,1,6,2,2,2,4,2,2,1,3,2,3,2,1,5,4,6,6,2,5,3,1,2,5,6,6,5,6,1,6,2,3,2,4,3,2,5,2,4,4,1,4,6,2,3,4,3,1,5,6,1,1,2,2,5,1,3,6,1,1,2,3,4,4,6,3,5,4,6,1,2,5,3,3,5,6,1,6,3,5,1,4,5,6,6,4,2,2,4,1,1,2,6,6,6,4,4,6,3,6,3,6,3,2,1,2,6,6,6,4,4,4,2,4,1,3,4,1,3,6,3,2,3,3,6,6,3,1,4,1,6,2,6,4,3,2,6,6,1,2,3,4,4,3,3,5,6,4,3,6,2,3,5,2,4,2,6,5,4,3,3,6,2,2,4,6,5,6,4,3,6,4,3,4,1,4,5,2,3,5,3,5,6,5,6,6,3,3,3,3,3,6,2,1,4,6,5,4,1,5,1,5,6,3,4,4,5,4,3,1,5,4,5,1,2,4,5,4,1,1,6,3,6,6,1,5,2,4,4,3,4,1,5,6,3,2,3,6,3,5,6,6,3,1,3,1,6,3,3,2,5,1,1,6,5,6,1,6,3,2,5,5,2,5,5,4,3,6,1,6,6,4,6,6,5,1,5,5,1,4,3,2,4,1,4,5,3,6,5,6,2,3,2,5,1,3,4,2,2,3,6,5,1,5,1,1,6,2,6,5,1,2,2,6,4,3,5,4,2,4,2,5,3,3,1,2,2,5,1,5,3,5,1,3,5,5,3,2,5,2,6,3,6,4,5,6,4,3,3,3,6,1,5,4,4,1,1,3,5,3,6,4,1,4,1,5,3,3,2,2,3,1,1,5,3,5,5,1,4,5,3,1,4,1,1,1,1,5,4,4,2,6,3,6,5,4,5,3,1,3,5,1,5,1,1,2,4,6,2,4,4,6,6,4,6,6,1,1,2,5,6,5,6,2,1,4,1,6,6,1,6,3,5,2,6,3,3,2,3,6,4,3,1,6,1,2,1,4,1,4,1,4,3,3,1,2,6,5,5,1,5,6,4,5,6,2,5,3,1,3,3,6,6,2,4,3,5,3,4,3,1,1,6,5,6,1,4,5,6,3,1,1,3,2,4,3,1,3,2,6,2,4,6,4,5,2,2,5,2,1,6,5,5,2,4,1,4,1,3,5,6,1,4,4,2,1,6,5,6,6,6,2,4,4,3,5,3,1,3,2,2,5,6,6,2,2,6,6,3,3,5,1,5,4,4,5,3,2,3,1,6,3,2,5,5,4,5,6,5,6,6,6,5,5,2,2,4,2,1,2,6,3,2,3,4,1,1,5,4,2,6,6,4,1,2,1,1,3,2,6,4,5,5,4,6,5,3,5,3,2,5,1,1,1,1,5,1,5,2,4,5,2,2,4,5,5,3,1,5,4,4,5,3,3,5,5,3,1,5,3,2,1,3,3,1,3,4,5,1,3,2,3,1,6,2,5,2 -------------------------------------------------------------------------------- /tests/shortestpath.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import unittest 3 | from pykernels.graph.shortestpath import ShortestPath 4 | from pykernels.graph.basic import Graph 5 | from graphutils import generate_testdata, generate_testanswers, generate_node_labels 6 | 7 | __author__ = 'kasiajanocha' 8 | 9 | """ 10 | A bunch of tests checking Shortest Path Kernel's compatibility with SPKernel implemented in [4]. 11 | """ 12 | 13 | class TestShortestPath(unittest.TestCase): 14 | def setUp(self): 15 | self.data = generate_testdata() 16 | self.answers_unlabeled = generate_testanswers('ShortestPath') 17 | self.answers_labeled = generate_testanswers('ShortestPathLabeled') 18 | labels = generate_node_labels() 19 | self.graphs = [] 20 | for i, g_table in enumerate(self.data): 21 | current_data = [] 22 | for j, g in enumerate(g_table): 23 | current_data.append(Graph(g, node_labels=labels[i][j])) 24 | self.graphs.append(current_data) 25 | 26 | def tearDown(self): 27 | pass 28 | 29 | def testSPKernel(self): 30 | K = ShortestPath() 31 | for i, data in enumerate(self.data): 32 | self.assertTrue((K.gram(data)==self.answers_unlabeled[i]).all()) 33 | for i, data in enumerate(self.graphs): 34 | self.assertTrue((K.gram(data)==self.answers_unlabeled[i]).all()) 35 | 36 | def testLabeledSPKernel(self): 37 | K = ShortestPath(labeled=True) 38 | for i, data in enumerate(self.graphs): 39 | self.assertTrue((K.gram(data)==self.answers_labeled[i]).all()) 40 | -------------------------------------------------------------------------------- /pykernels/graph/basic.py: -------------------------------------------------------------------------------- 1 | """ 2 | A module containing basic operations on graphs. 3 | """ 4 | __author__ = 'kasiajanocha' 5 | 6 | import numpy as np 7 | 8 | class Graph(object): 9 | """Basic Graph class. 10 | Can be labeled by edges or nodes.""" 11 | def __init__(self, adjacency_matix, node_labels=None, edge_labels=None): 12 | self.adjacency_matix = adjacency_matix 13 | self.node_labels = node_labels 14 | self.edge_labels = edge_labels 15 | 16 | def graphs_to_adjacency_lists(data): 17 | """ 18 | Given a list of graphs, output a numpy.array 19 | containing their adjacency matices. 20 | """ 21 | try: 22 | if data.ndim == 3: 23 | return np.array(data) 24 | except Exception, exc: 25 | try: 26 | return np.array([G.adjacency_matix for G in data]) 27 | except Exception, exc: 28 | return np.array(data) 29 | 30 | def relabel(data, data_2): 31 | """ 32 | Given list of labels for each graph in the dataset, 33 | rename them so they belong to set {1, ..., num_labels}, 34 | where num_labels is number of the distinct labels. 35 | Return tuple consisting of new labels and maximal label. 36 | """ 37 | len_first = len(data) 38 | for d in data_2: 39 | data.append(d) 40 | data = np.array(data) 41 | label_set = dict() 42 | for node_labels in data: 43 | for label in node_labels: 44 | if label not in label_set.keys(): 45 | llen = len(label_set) 46 | label_set[label] = llen 47 | res = [] 48 | for i, node_labels in enumerate(data): 49 | res.append([]) 50 | for j, label in enumerate(node_labels): 51 | res[i].append(label_set[label] + 1) 52 | return res[:len_first], res[len_first:], len(label_set) 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![pyKernels](/doc/img/logo.png?raw=true "pyKernels") 2 | 3 | * authors: Wojciech Marian Czarnecki and Katarzyna Janocha 4 | * version: 0.0.4 5 | * dependencies: numpy, scipy, scikit-learn 6 | 7 | ## General description 8 | Python library for working with kernel methods in machine learning. 9 | Provided code is easy to use set of implementations of various 10 | kernel functions ranging from typical linear, polynomial or 11 | rbf ones through wawelet, fourier transformations, kernels 12 | for binary sequences and even kernels for labeled graphs. 13 | 14 | ## Sample usage 15 | 16 | from sklearn.svm import SVC 17 | from sklearn.metrics import accuracy_score 18 | import numpy as np 19 | 20 | from pykernels.basic import RBF 21 | 22 | X = np.array([[1,1], [0,0], [1,0], [0,1]]) 23 | y = np.array([1, 1, 0, 0]) 24 | 25 | print 'Testing XOR' 26 | 27 | for clf, name in [(SVC(kernel=RBF(), C=1000), 'pykernel'), (SVC(kernel='rbf', C=1000), 'sklearn')]: 28 | clf.fit(X, y) 29 | print name 30 | print clf 31 | print 'Predictions:', clf.predict(X) 32 | print 'Accuracy:', accuracy_score(clf.predict(X), y) 33 | print 34 | 35 | ## implemented Kernels 36 | 37 | * Vector kernels for R^d 38 | * Linear 39 | * Polynomial 40 | * RBF 41 | * Cosine similarity 42 | * Exponential 43 | * Laplacian 44 | * Rational quadratic 45 | * Inverse multiquadratic 46 | * Cauchy 47 | * T-Student 48 | * ANOVA 49 | * Additive Chi^2 50 | * Chi^2 51 | * MinMax 52 | * Min/Histogram intersection 53 | * Generalized histogram intersection 54 | * Spline 55 | * Sorensen 56 | * Tanimoto 57 | * Wavelet 58 | * Fourier 59 | * Log (CPD) 60 | * Power (CPD) 61 | 62 | * Graph kernels 63 | * Labeled 64 | * Shortest paths 65 | 66 | * Unlabeled 67 | * Shortest paths 68 | * 3,4-Graphlets 69 | * Random walk 70 | -------------------------------------------------------------------------------- /pykernels/basic.py: -------------------------------------------------------------------------------- 1 | """ 2 | Collection of basic kernel functions, which can be found in nearly any ML 3 | library 4 | """ 5 | 6 | __author__ = 'lejlot' 7 | 8 | from pykernels.base import Kernel 9 | import numpy as np 10 | from utils import euclidean_dist_matrix 11 | 12 | class Linear(Kernel): 13 | """ 14 | Linear kernel, defined as a dot product between vectors 15 | 16 | K(x, y) = 17 | """ 18 | 19 | def __init__(self): 20 | self._dim = None 21 | 22 | def _compute(self, data_1, data_2): 23 | self._dim = data_1.shape[1] 24 | return data_1.dot(data_2.T) 25 | 26 | def dim(self): 27 | return self._dim 28 | 29 | 30 | class Polynomial(Kernel): 31 | """ 32 | Polynomial kernel, defined as a power of an affine transformation 33 | 34 | K(x, y) = (a + b)^p 35 | 36 | where: 37 | a = scale 38 | b = bias 39 | p = degree 40 | """ 41 | 42 | def __init__(self, scale=1, bias=0, degree=2): 43 | self._dim = None 44 | self._scale = scale 45 | self._bias = bias 46 | self._degree = degree 47 | 48 | def _compute(self, data_1, data_2): 49 | self._dim = data_1.shape[1] 50 | return (self._scale * data_1.dot(data_2.T) + self._bias) ** self._degree 51 | 52 | def dim(self): 53 | return self._dim ** self._degree 54 | 55 | class RBF(Kernel): 56 | """ 57 | Radial Basis Function kernel, defined as unnormalized Gaussian PDF 58 | 59 | K(x, y) = e^(-g||x - y||^2) 60 | 61 | where: 62 | g = gamma 63 | """ 64 | 65 | def __init__(self, gamma=None): 66 | self._gamma = gamma 67 | 68 | def _compute(self, data_1, data_2): 69 | if self._gamma is None: 70 | # libSVM heuristics 71 | self._gamma = 1./data_1.shape[1] 72 | 73 | dists_sq = euclidean_dist_matrix(data_1, data_2) 74 | return np.exp(-self._gamma * dists_sq) 75 | 76 | def dim(self): 77 | return np.inf 78 | 79 | -------------------------------------------------------------------------------- /pykernels/graph/randomwalk.py: -------------------------------------------------------------------------------- 1 | """ 2 | A module containing Random Walk Kernel. 3 | """ 4 | 5 | __author__ = 'kasiajanocha' 6 | 7 | import numpy as np 8 | from pykernels.base import Kernel, GraphKernel 9 | from scipy.sparse import lil_matrix, kron,identity 10 | from scipy.sparse.linalg import lsqr 11 | import basic 12 | 13 | def _norm(adj_mat): 14 | """Normalize adjacency matrix""" 15 | norm = adj_mat.sum(axis=0) 16 | norm[norm == 0] = 1 17 | return adj_mat / norm 18 | 19 | class RandomWalk(GraphKernel): 20 | """ 21 | Unlabeled random walk kernel [1] 22 | using conjugate gradient method 23 | """ 24 | 25 | def __init__(self, lmb=0.5, tolerance=1e-8, maxiter=20): 26 | self._lmb = lmb 27 | self._tolerance = tolerance 28 | self._max_iter = maxiter 29 | 30 | # either tensor of dimention 3 (list of adjacency matrices) 31 | def _compute(self, data_1, data_2): 32 | data_1 = basic.graphs_to_adjacency_lists(data_1) 33 | data_2 = basic.graphs_to_adjacency_lists(data_2) 34 | res = np.zeros((len(data_1), len(data_2))) 35 | N = len(data_1) * len(data_2) 36 | for i, graph1 in enumerate(data_1): 37 | for j, graph2 in enumerate(data_2): 38 | # norm1, norm2 - normalized adjacency matrixes 39 | norm1 = _norm(graph1) 40 | norm2 = _norm(graph2) 41 | # if graph is unweighted, W_prod = kron(a_norm(g1)*a_norm(g2)) 42 | w_prod = kron(lil_matrix(norm1), lil_matrix(norm2)) 43 | starting_prob = np.ones(w_prod.shape[0]) / (w_prod.shape[0]) 44 | stop_prob = starting_prob 45 | # first solve (I - lambda * W_prod) * x = p_prod 46 | A = identity(w_prod.shape[0]) - (w_prod * self._lmb) 47 | x = lsqr(A, starting_prob) 48 | res[i, j] = stop_prob.T.dot(x[0]) 49 | # print float(len(data_2)*i + j)/float(N), "%" 50 | return res 51 | 52 | def dim(self): 53 | return None 54 | -------------------------------------------------------------------------------- /tests/basic.py: -------------------------------------------------------------------------------- 1 | from sklearn.svm import SVC 2 | import numpy as np 3 | from pykernels.basic import Linear, Polynomial, RBF 4 | from datasets import baseline_logic 5 | from operator import add as logical_or, mul as logical_and 6 | import unittest 7 | 8 | __author__ = 'lejlot' 9 | 10 | class TestSimpleLogicWithSklearnSVM(unittest.TestCase): 11 | """ 12 | Tets whether sklearn SVM behaves identically using its 13 | internal implementation of basic kernels and our implementation 14 | when facing simple binary logic problems 15 | """ 16 | 17 | def setUp(self): 18 | self.datasets = [baseline_logic(operator) for operator in 19 | (logical_or, logical_and)] 20 | self.models = [(SVC(kernel=Linear(), C=1000), 21 | SVC(kernel='linear', C=1000)), 22 | 23 | (SVC(kernel=Polynomial(bias=1, degree=2), C=1000), 24 | SVC(kernel='poly', C=1000, coef0=1, degree=2)), 25 | 26 | (SVC(kernel=RBF(), C=1000), 27 | SVC(kernel='rbf', C=1000))] 28 | 29 | def tearDown(self): 30 | pass 31 | 32 | def test_train_predictions(self): 33 | """ Checks whether predictions on the train set are identical """ 34 | for X, y in self.datasets: 35 | for model_list in self.models: 36 | predictions = [] 37 | predictions_single = [] 38 | for model in model_list: 39 | model.fit(X, y) 40 | predictions.append(model.predict(X).tolist()) 41 | predictions_single.append(model.predict(X[0]).tolist()) 42 | self.assertEqual(*predictions) 43 | self.assertEqual(*predictions_single) 44 | 45 | 46 | def test_support_vectors(self): 47 | """ Checks whether set of support vectors are identical """ 48 | for X, y in self.datasets: 49 | for model_list in self.models: 50 | supports = [] 51 | for model in model_list: 52 | model.fit(X, y) 53 | support = model.support_vectors_ 54 | if support.shape[0] > 0: 55 | supports.append(support.ravel().tolist()) 56 | else: 57 | supports.append(X[model.support_].ravel().tolist()) 58 | self.assertEqual(*supports) 59 | 60 | 61 | if __name__ == '__main__': 62 | unittest.main(verbosity=3) 63 | -------------------------------------------------------------------------------- /tests/psd.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains tests connected with Mercer's theorem 3 | """ 4 | 5 | __author__ = 'lejlot' 6 | 7 | import numpy as np 8 | from pykernels.basic import Linear, Polynomial, RBF 9 | from pykernels.regular import * 10 | from pykernels.graph.randomwalk import RandomWalk 11 | from pykernels.graph.allgraphlets import All34Graphlets 12 | from pykernels.graph.shortestpath import ShortestPath 13 | from pykernels.base import Kernel, GraphKernel 14 | import unittest 15 | from scipy import linalg as la 16 | import inspect 17 | 18 | def find_all_children(parent_class): 19 | """ 20 | Returns list of references to all loaded classes that 21 | inherit from parent_class 22 | """ 23 | import sys, inspect 24 | subclasses = [] 25 | callers_module = sys._getframe(1).f_globals['__name__'] 26 | classes = inspect.getmembers(sys.modules[callers_module], inspect.isclass) 27 | for name, obj in classes: 28 | if (obj is not parent_class) and (parent_class in inspect.getmro(obj)): 29 | subclasses.append((obj, name)) 30 | return subclasses 31 | 32 | class TestPositiveDefinitness(unittest.TestCase): 33 | """ 34 | According to Mercer's theorem, K is a kernel if and only if 35 | resulting Gramian is positive semi-definite. This tests 36 | generate random vectors, compute Gramians and check if they 37 | are PSD. 38 | """ 39 | 40 | def setUp(self): 41 | self.tol = 1e-8 42 | 43 | def get_data(self, kernel): 44 | """ 45 | Prepares set of datasets for a particular kernel 46 | """ 47 | 48 | np.random.seed(0) 49 | 50 | if kernel is All34Graphlets: 51 | return [[np.array([[ 1, 1, 0, 0], 52 | [ 1, 1, 0, 1], 53 | [ 0, 0, 1, 0], 54 | [ 0, 1, 0, 1]])], 55 | [np.array([[ 1, 0, 0, 1], 56 | [ 0, 1, 0, 0], 57 | [ 0, 0, 1, 0], 58 | [ 1, 0, 0, 1]])]] 59 | 60 | elif GraphKernel in kernel.__mro__: 61 | return [[np.array([[ 1, 1], [ 1, 1]])], 62 | [np.array([[ 1, 0],[ 0, 1]])], 63 | [np.array([[ 1, 1, 0, 0], 64 | [ 1, 1, 0, 1], 65 | [ 0, 0, 1, 0], 66 | [ 0, 1, 0, 1]])], 67 | [np.array([[ 1, 0, 0, 1], 68 | [ 0, 1, 0, 0], 69 | [ 0, 0, 1, 0], 70 | [ 1, 0, 0, 1]])]] 71 | elif PositiveKernel in kernel.__mro__: 72 | def _pos(x): 73 | return np.abs(x) + 10e-5 74 | return [_pos(np.random.randn(100, 20)), _pos(np.random.randn(500, 2)), 75 | _pos(np.random.randn(10, 100)), _pos(np.random.rand(100, 20)), 76 | _pos(np.random.rand(500, 100)), _pos(np.random.rand(3, 1000))] 77 | elif ConditionalyPositiveDefiniteKernel in kernel.__mro__: 78 | return [] # This test is now suited for CPD kernels 79 | else: 80 | return [np.random.randn(100, 20), np.random.randn(500, 2), 81 | np.random.randn(10, 100), np.random.rand(100, 20), 82 | np.random.rand(500, 100), np.random.rand(3, 1000)] 83 | 84 | def tearDown(self): 85 | pass 86 | 87 | def testPSD(self): 88 | kernels = find_all_children(Kernel) 89 | for kernel, _ in kernels: 90 | if not inspect.isabstract(kernel): # ignore abstract classes 91 | for data in self.get_data(kernel): 92 | eigens, _ = la.eigh(kernel().gram(data)) 93 | self.assertTrue(np.all(eigens > -self.tol)) 94 | 95 | if __name__ == '__main__': 96 | unittest.main(verbosity=3) 97 | -------------------------------------------------------------------------------- /tests/graphutils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import unittest 3 | from pykernels.graph.shortestpath import floyd_warshall 4 | from pykernels.graph import allgraphlets 5 | from pykernels.graph.basic import relabel 6 | 7 | __author__ = 'kasiajanocha' 8 | 9 | """ 10 | Tests for shared graph util functions such as Floyd Warshall computation or graphlet creation 11 | together with shared test methods. 12 | """ 13 | 14 | def generate_testdata(): 15 | tdata = [[np.genfromtxt('tests/data/random_10_node.csv',delimiter=',').reshape(10,10)], 16 | [np.genfromtxt('tests/data/random_100_node.csv',delimiter=',').reshape(100,100)], 17 | np.genfromtxt('tests/data/8_4x4_ams.csv',delimiter=',').reshape(8,4,4), 18 | np.genfromtxt('tests/data/8_100x100_ams.csv',delimiter=',').reshape(8,100,100)] 19 | return np.array(tdata) 20 | 21 | def generate_testanswers(name): 22 | ans = [np.genfromtxt('tests/data/answers/' + name + '/random_10_node.csv',delimiter=',').reshape(1,1), 23 | np.genfromtxt('tests/data/answers/' + name + '/random_100_node.csv',delimiter=',').reshape(1,1), 24 | np.genfromtxt('tests/data/answers/' + name + '/8_4x4_ams.csv',delimiter=',').reshape(8,8), 25 | np.genfromtxt('tests/data/answers/' + name + '/8_100x100_ams.csv',delimiter=',').reshape(8,8)] 26 | return np.array(ans) 27 | 28 | def generate_node_labels(): 29 | tdata = [np.genfromtxt('tests/data/random_10_node_labels.csv',delimiter=',').reshape(1,10), 30 | np.genfromtxt('tests/data/random_100_node_labels.csv',delimiter=',').reshape(1,100), 31 | np.genfromtxt('tests/data/8_4x4_ams_labels.csv',delimiter=',').reshape(8,4), 32 | np.genfromtxt('tests/data/8_100x100_ams_labels.csv',delimiter=',').reshape(8,100)] 33 | return np.array(tdata) 34 | 35 | class TestFloydWarshall(unittest.TestCase): 36 | # TODO(kasiajanocha): add test cases 37 | def setUp(self): 38 | t = np.ones((100,100)) 39 | np.fill_diagonal(t, 0) 40 | self.datasets = [np.array([[1,1],[1,1]]), 41 | np.ones((100,100))] 42 | self.results = [np.array([[0,1],[1,0]]), 43 | t] 44 | 45 | def tearDown(self): 46 | pass 47 | 48 | def testFloydWarshall(self): 49 | for i, g in enumerate(self.datasets): 50 | self.assertTrue((floyd_warshall(g,g) == self.results[i]).all()) 51 | 52 | class TestGraphletCreation(unittest.TestCase): 53 | def setUp(self): 54 | self.all_3_graphlets = np.array([[[1,1,1],[1,1,1],[1,1,1]], 55 | [[1,0,1],[0,1,1],[1,1,1]], 56 | [[1,0,0],[0,1,1],[0,1,1]], 57 | [[1,0,0],[0,1,0],[0,0,1]]]) 58 | 59 | def tearDown(self): 60 | pass 61 | 62 | def _contains_values(self, a1, a2): 63 | for v in a1: 64 | if not v in a2: 65 | return False 66 | return True 67 | 68 | def test3GraphletsCreation(self): 69 | gr3 = allgraphlets._generate_graphlets(3, None) 70 | self.assertTrue(gr3.shape[0] == 4) 71 | self.assertTrue(self._contains_values(self.all_3_graphlets, gr3)) 72 | 73 | def test4GraphletsCreation(self): 74 | gr4 = allgraphlets._generate_graphlets(4, self.all_3_graphlets) 75 | self.assertTrue(gr4.shape[0] == 11) 76 | 77 | class TestRelabel(unittest.TestCase): 78 | def setUp(self): 79 | self.data = [[["l0","l1","l0"],["l1","l1","l0"],["l2","l2","l2"]], 80 | [[1,10,30,1,60]], 81 | [[0,0],[0,0]]] 82 | self.answers = [[[1,2,1],[2,2,1],[3,3,3]], 83 | [[1,2,3,1,4]], 84 | [[1,1],[1,1]]] 85 | self.answers = np.array(self.answers) 86 | 87 | def tearDown(self): 88 | pass 89 | 90 | def testRelabel(self): 91 | for i, data in enumerate(self.data): 92 | self.assertTrue((relabel(data) == self.answers[i]).all()) -------------------------------------------------------------------------------- /pykernels/graph/shortestpath.py: -------------------------------------------------------------------------------- 1 | """ 2 | A module containing Shortest Path Kernel. 3 | """ 4 | __author__ = 'kasiajanocha' 5 | 6 | import numpy as np 7 | import numpy.matlib as matlib 8 | import basic 9 | from pykernels.base import Kernel, GraphKernel 10 | from scipy.sparse import lil_matrix 11 | 12 | def floyd_warshall(adj_mat, weights): 13 | """ 14 | Returns matrix of shortest path weights. 15 | """ 16 | N = adj_mat.shape[0] 17 | res = np.zeros((N, N)) 18 | res = res + ((adj_mat != 0) * weights) 19 | res[res == 0] = np.inf 20 | np.fill_diagonal(res, 0) 21 | for i in xrange(N): 22 | for j in xrange(N): 23 | for k in xrange(N): 24 | if res[i, j] + res[j, k] < res[i, k]: 25 | res[i, k] = res[i, j] + res[j, k] 26 | return res 27 | 28 | def _apply_floyd_warshall(data): 29 | """ 30 | Applies Floyd-Warshall algorithm on a dataset. 31 | Returns a tuple containing dataset of FW transformates and max path length 32 | """ 33 | res = [] 34 | maximal = 0 35 | for graph in data: 36 | floyd = floyd_warshall(graph, graph) 37 | maximal = max(maximal, (floyd[~np.isinf(floyd)]).max()) 38 | res.append(floyd) 39 | return res, maximal 40 | 41 | class ShortestPath(GraphKernel): 42 | """ 43 | Shortest Path kernel [3] 44 | """ 45 | def __init__(self, labeled=False): 46 | self.labeled = labeled 47 | 48 | def _create_accum_list_labeled(self, shortest_paths, maxpath, 49 | labels_t, numlabels): 50 | """ 51 | Construct accumulation array matrix for one dataset 52 | containing labaled graph data. 53 | """ 54 | res = lil_matrix( 55 | np.zeros((len(shortest_paths), 56 | (maxpath + 1) * numlabels * (numlabels + 1) / 2))) 57 | for i, s in enumerate(shortest_paths): 58 | labels = labels_t[i] 59 | labels_aux = matlib.repmat(labels, 1, len(labels)) 60 | min_lab = np.minimum(labels_aux.T, labels_aux) 61 | max_lab = np.maximum(labels_aux.T, labels_aux) 62 | subsetter = np.triu(~(np.isinf(s))) 63 | min_lab = min_lab[subsetter] 64 | max_lab = max_lab[subsetter] 65 | ind = s[subsetter] * numlabels * (numlabels + 1) / 2 + \ 66 | (min_lab - 1) * (2*numlabels + 2 - min_lab) / 2 + \ 67 | max_lab - min_lab 68 | accum = np.zeros((maxpath + 1) * numlabels * (numlabels + 1) / 2) 69 | accum[:ind.max() + 1] += np.bincount(ind.astype(int)) 70 | res[i] = lil_matrix(accum) 71 | return res 72 | 73 | def _create_accum_list(self, shortest_paths, maxpath): 74 | """ 75 | Construct accumulation array matrix for one dataset 76 | containing unlabaled graph data. 77 | """ 78 | res = lil_matrix(np.zeros((len(shortest_paths), maxpath+1))) 79 | for i, s in enumerate(shortest_paths): 80 | subsetter = np.triu(~(np.isinf(s))) 81 | ind = s[subsetter] 82 | accum = np.zeros(maxpath + 1) 83 | accum[:ind.max() + 1] += np.bincount(ind.astype(int)) 84 | res[i] = lil_matrix(accum) 85 | return res 86 | 87 | def _compute(self, data_1, data_2): 88 | ams_1 = basic.graphs_to_adjacency_lists(data_1) 89 | ams_2 = basic.graphs_to_adjacency_lists(data_2) 90 | sp_1, max1 = _apply_floyd_warshall(np.array(ams_1)) 91 | sp_2, max2 = _apply_floyd_warshall(np.array(ams_2)) 92 | maxpath = max(max1, max2) 93 | if not self.labeled: 94 | accum_list_1 = self._create_accum_list(sp_1, maxpath) 95 | accum_list_2 = self._create_accum_list(sp_2, maxpath) 96 | else: 97 | labels_1, labels_2, numlabels = basic.relabel( 98 | [G.node_labels for G in data_1], [G.node_labels for G in data_2]) 99 | accum_list_1 = self._create_accum_list_labeled(sp_1, maxpath, 100 | labels_1, numlabels) 101 | accum_list_2 = self._create_accum_list_labeled(sp_2, maxpath, 102 | labels_2, numlabels) 103 | return np.asarray(accum_list_1.dot(accum_list_2.T).todense()) 104 | 105 | def dim(self): 106 | return None 107 | -------------------------------------------------------------------------------- /pykernels/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Base classes and methods used by all kernels 3 | """ 4 | 5 | __author__ = 'lejlot' 6 | 7 | import numpy as np 8 | from abc import abstractmethod, ABCMeta 9 | 10 | class Kernel(object): 11 | """ 12 | Base, abstract kernel class 13 | """ 14 | __metaclass__ = ABCMeta 15 | 16 | def __call__(self, data_1, data_2): 17 | return self._compute(data_1, data_2) 18 | 19 | @abstractmethod 20 | def _compute(self, data_1, data_2): 21 | """ 22 | Main method which given two lists data_1 and data_2, with 23 | N and M elements respectively should return a kernel matrix 24 | of size N x M where K_{ij} = K(data_1_i, data_2_j) 25 | """ 26 | raise NotImplementedError('This is an abstract class') 27 | 28 | def gram(self, data): 29 | """ 30 | Returns a Gramian, kernel matrix of matrix and itself 31 | """ 32 | return self._compute(data, data) 33 | 34 | @abstractmethod 35 | def dim(self): 36 | """ 37 | Returns dimension of the feature space 38 | """ 39 | raise NotImplementedError('This is an abstract class') 40 | 41 | def __str__(self): 42 | return self.__class__.__name__ 43 | 44 | def __repr__(self): 45 | return str(self) 46 | 47 | def __add__(self, kernel): 48 | return KernelSum(self, kernel) 49 | 50 | def __mul__(self, value): 51 | if isinstance(value, Kernel): 52 | return KernelProduct(self, value) 53 | else: 54 | if isinstance(self, ScaledKernel): 55 | return ScaledKernel(self._kernel, self._scale * value) 56 | else: 57 | return ScaledKernel(self, value) 58 | 59 | def __rmul__(self, value): 60 | return self.__mul__(value) 61 | 62 | def __div__(self, scale): 63 | return ScaledKernel(self, 1./scale) 64 | 65 | def __pow__(self, value): 66 | return KernelPower(self, value) 67 | 68 | class KernelSum(Kernel): 69 | """ 70 | Represents sum of a pair of kernels 71 | """ 72 | 73 | def __init__(self, kernel_1, kernel_2): 74 | self._kernel_1 = kernel_1 75 | self._kernel_2 = kernel_2 76 | 77 | def _compute(self, data_1, data_2): 78 | return self._kernel_1._compute(data_1, data_2) + \ 79 | self._kernel_2._compute(data_1, data_2) 80 | 81 | def dim(self): 82 | # It is too complex to analyze combined dimensionality, so we give a lower bound 83 | return max(self._kernel_1.dim(), self._kernel_2.dim()) 84 | 85 | def __str__(self): 86 | return '(' + str(self._kernel_1) + ' + ' + str(self._kernel_2) + ')' 87 | 88 | 89 | class KernelProduct(Kernel): 90 | """ 91 | Represents product of a pair of kernels 92 | """ 93 | 94 | def __init__(self, kernel_1, kernel_2): 95 | self._kernel_1 = kernel_1 96 | self._kernel_2 = kernel_2 97 | 98 | def _compute(self, data_1, data_2): 99 | return self._kernel_1._compute(data_1, data_2) * \ 100 | self._kernel_2._compute(data_1, data_2) 101 | 102 | def dim(self): 103 | # It is too complex to analyze combined dimensionality, so we give a lower bound 104 | return max(self._kernel_1.dim(), self._kernel_2.dim()) 105 | 106 | def __str__(self): 107 | return '(' + str(self._kernel_1) + ' * ' + str(self._kernel_2) + ')' 108 | 109 | 110 | class KernelPower(Kernel): 111 | """ 112 | Represents natural power of a kernel 113 | """ 114 | 115 | def __init__(self, kernel, d): 116 | self._kernel = kernel 117 | self._d = d 118 | if not isinstance(d, int) or d<0: 119 | raise Exception('Kernel power is only defined for non-negative integer degrees') 120 | 121 | def _compute(self, data_1, data_2): 122 | return self._kernel._compute(data_1, data_2) ** self._d 123 | 124 | def dim(self): 125 | # It is too complex to analyze combined dimensionality, so we give a lower bound 126 | return self._kernel.dim() 127 | 128 | def __str__(self): 129 | return str(self._kernel) + '^' + str(self._d) 130 | 131 | 132 | class ScaledKernel(Kernel): 133 | """ 134 | Represents kernel scaled by a float 135 | """ 136 | 137 | def __init__(self, kernel, scale): 138 | self._kernel = kernel 139 | self._scale = scale 140 | if scale < 0: 141 | raise Exception('Negation of the kernel is not a kernel!') 142 | 143 | def _compute(self, data_1, data_2): 144 | return self._scale * self._kernel._compute(data_1, data_2) 145 | 146 | def dim(self): 147 | return self._kernel.dim() 148 | 149 | def __str__(self): 150 | if self._scale == 1.0: 151 | return str(self._kernel) 152 | else: 153 | return str(self._scale) + ' ' + str(self._kernel) 154 | 155 | 156 | class GraphKernel(Kernel): 157 | """ 158 | Base, abstract GraphKernel kernel class 159 | """ 160 | pass 161 | -------------------------------------------------------------------------------- /pykernels/graph/allgraphlets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Graphlet kernels 3 | """ 4 | 5 | __author__ = 'kasiajanocha' 6 | 7 | import itertools 8 | import numpy as np 9 | from pykernels.base import Kernel, GraphKernel 10 | import basic 11 | 12 | def dec2bin(k, bitlength=0): 13 | """Decimal to binary""" 14 | return [1 if digit == '1' else 0 for digit in bin(k)[2:].zfill(bitlength)] 15 | 16 | def _number_of_graphlets(size): 17 | """Number of all undirected graphlets of given size""" 18 | if size == 2: 19 | return 2 20 | if size == 3: 21 | return 4 22 | if size == 4: 23 | return 11 24 | if size == 5: 25 | return 34 26 | 27 | def _generate_graphlets(size): 28 | """Generates graphlet array from previously stored csv data""" 29 | if size == 3: 30 | return np.genfromtxt('pykernels/graph/data/3graphlets.csv', 31 | delimiter=',').reshape(4, 3, 3) 32 | elif size == 4: 33 | return np.genfromtxt('pykernels/graph/data/4graphlets.csv', 34 | delimiter=',').reshape(11, 4, 4) 35 | 36 | def _is_3star(adj_mat): 37 | """Check if a given graphlet of size 4 is a 3-star""" 38 | return (adj_mat.sum() == 10 and 4 in [a.sum() for a in adj_mat]) 39 | 40 | def _4_graphlet_contains_3star(adj_mat): 41 | """Check if a given graphlet of size 4 contains a 3-star""" 42 | return (4 in [a.sum() for a in adj_mat]) 43 | 44 | def _compare_graphlets(am1, am2): 45 | """ 46 | Compare two graphlets. 47 | """ 48 | adj_mat1 = am1 49 | adj_mat2 = am2 50 | np.fill_diagonal(adj_mat1, 1) 51 | np.fill_diagonal(adj_mat2, 1) 52 | k = np.array(adj_mat1).shape[0] 53 | if k == 3: 54 | # the number of edges determines isomorphism of graphs of size 3. 55 | return np.array(adj_mat1).sum() == np.array(adj_mat2).sum() 56 | else: 57 | # (k-1) graphlet count determines graph isomorphism for small graphs 58 | # return (_count_graphlets(adj_mat1, k-1, graphlet3_array, None) == 59 | # _count_graphlets(adj_mat2, k-1, graphlet3_array, None)).all() 60 | if not np.array(adj_mat1).sum() == np.array(adj_mat2).sum(): 61 | return False 62 | if np.array(adj_mat1).sum() in (4, 6, 14, 16): 63 | # 0, 1, 5 or 6 edges 64 | return True 65 | if np.array(adj_mat1).sum() == 8: 66 | # 2 edges - two pairs or 2-path 67 | return 3.0 in [adj_mat.sum() for adj_mat in adj_mat1] == \ 68 | 3.0 in [adj_mat.sum() for adj_mat in adj_mat2] 69 | if np.array(adj_mat1).sum() == 10: 70 | # 3 edges - 3-star, 3-path or 3-cycle 71 | sums1 = [adj_mat.sum() for adj_mat in adj_mat1] 72 | sums2 = [adj_mat.sum() for adj_mat in adj_mat2] 73 | if (_is_3star(adj_mat1) + _is_3star(adj_mat2))%2 == 1: 74 | return False 75 | if _is_3star(adj_mat1) and _is_3star(adj_mat2): 76 | return True 77 | return (1 in sums1) == (1 in sums2) 78 | if np.array(adj_mat1).sum() == 12: 79 | # 4 edges - a simple cycle or something containing 3-star 80 | return _4_graphlet_contains_3star(adj_mat1) == \ 81 | _4_graphlet_contains_3star(adj_mat2) 82 | 83 | return False 84 | 85 | def _graphlet_index(adj_mat, graphlet_array): 86 | """Return index to increment.""" 87 | for i, g in enumerate(graphlet_array): 88 | if _compare_graphlets(adj_mat, g): 89 | return i 90 | return -1 91 | 92 | def _count_graphlets(adj_mat, size, graphlet_array): 93 | """Count all graphlets of given size""" 94 | adj_mat = np.array(adj_mat) 95 | res = np.zeros((1, _number_of_graphlets(size))) 96 | for subset in itertools.combinations(range(adj_mat.shape[0]), size): 97 | graphlet = (adj_mat[subset, :])[:, subset] 98 | res[0][_graphlet_index(graphlet, graphlet_array)] += 1 99 | # print "returning ", res / sum(sum(res)) 100 | return res / res.sum() 101 | 102 | class All34Graphlets(GraphKernel): 103 | """ 104 | All-graphlets kernel [2] 105 | for 3,4 graphlets 106 | for undirected graphs 107 | 108 | k - size of graphlets 109 | """ 110 | def __init__(self, k=3): 111 | if k != 3 and k != 4: 112 | raise Exception('k should be 3 or 4.') 113 | self.k = k 114 | self.graphlet_array = _generate_graphlets(k) 115 | 116 | def _compute(self, data_1, data_2): 117 | data_1 = basic.graphs_to_adjacency_lists(data_1) 118 | data_2 = basic.graphs_to_adjacency_lists(data_2) 119 | d1 = np.zeros((data_1.shape[0], _number_of_graphlets(self.k))) 120 | d2 = np.zeros((data_2.shape[0], _number_of_graphlets(self.k))) 121 | for i, g in enumerate(data_1): 122 | d1[i] = _count_graphlets(g, self.k, self.graphlet_array) 123 | for i, g in enumerate(data_2): 124 | d2[i] = _count_graphlets(g, self.k, self.graphlet_array) 125 | return d1.dot(d2.T) 126 | 127 | def dim(self): 128 | return None 129 | -------------------------------------------------------------------------------- /tests/graphlets.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import unittest 3 | from pykernels.graph.allgraphlets import All34Graphlets 4 | from pykernels.graph import allgraphlets 5 | from graphutils import generate_testdata, generate_testanswers 6 | from scipy.misc import comb 7 | 8 | __author__ = 'kasiajanocha' 9 | 10 | """ 11 | A bunch of tests checking All34Graphlets Kernel's compatibility with allgraphlets implemented in [4], 12 | together with a simple (manually checkable) test. 13 | """ 14 | 15 | sampledata = np.array([[[1,1,1,1,1], 16 | [1,1,0,0,1], 17 | [1,0,1,1,1], 18 | [1,0,1,1,1], 19 | [1,1,1,1,1]], 20 | 21 | [[1,1,0,0,1], 22 | [1,1,1,0,0], 23 | [0,1,1,1,0], 24 | [0,0,1,1,1], 25 | [1,0,0,1,1]], 26 | 27 | [[1,1,0,0,1], 28 | [1,1,0,0,1], 29 | [0,0,1,1,0], 30 | [0,0,1,1,0], 31 | [1,1,0,0,1]], 32 | 33 | [[1,0,1,0,0], 34 | [0,1,1,0,0], 35 | [1,1,1,1,1], 36 | [0,0,1,1,0], 37 | [0,0,1,0,1]]]) 38 | 39 | samplecounts = np.array([[0.,1.,4.,5.], 40 | [0.,5.,5.,0.], 41 | [0.,9.,0.,1.], 42 | [4.,0.,6.,0.]]) / 10 43 | 44 | # graphlet counts: 45 | # 0,1,4,5 46 | # 0,5,5,0 47 | # 0,9,0,1 48 | # 4,0,6,0 49 | 50 | class TestGraphletSimple(unittest.TestCase): 51 | def setUp(self): 52 | self.data = sampledata 53 | self.answers = samplecounts.dot(samplecounts.T) 54 | self.data = np.array(self.data) 55 | self.answers = np.array(self.answers) 56 | 57 | def tearDown(self): 58 | pass 59 | 60 | def testSimple3Graphlets(self): 61 | K = All34Graphlets(3) 62 | self.assertTrue((K.gram(self.data)==self.answers).all()) 63 | 64 | class TestCountGraphlets(unittest.TestCase): 65 | def setUp(self): 66 | self.data = sampledata 67 | self.answers = samplecounts 68 | self.data = np.array(self.data) 69 | self.answers = np.array(self.answers) 70 | 71 | def tearDown(self): 72 | pass 73 | 74 | def testCount3Graphlets(self): 75 | graphlets = allgraphlets._generate_graphlets(3) 76 | for i, data in enumerate(self.data): 77 | self.assertTrue((allgraphlets._count_graphlets(data, 3, graphlets)==self.answers[i]).all()) 78 | 79 | class TestGraphlet(unittest.TestCase): 80 | """docstring for ClassName""" 81 | 82 | def clique_and_anti(self, n, k): 83 | """Returns an adjacency matrix of a graph being a concatenation of a kxk clique and (n-k)x(n-k) anticlique""" 84 | res = np.zeros((n,n)) 85 | np.fill_diagonal(res, 1) 86 | for i in range(k): 87 | for j in range(k): 88 | res[i][j] = 1 89 | return res 90 | 91 | def graphlets_counts_3(self, n,k): 92 | res = np.array([comb(n-k,3) + comb(n-k,2)*k, #empty 93 | comb(k,2)*(n-k), #one edge 94 | 0.0, # two edges 95 | comb(k,3)]) #triangles 96 | return (res/comb(n,3)) 97 | 98 | def graphlets_counts_4(self, n, k): 99 | res = np.array([comb(k,4), # cliques 100 | comb(k,3)*(n-k), #triangle + dot 101 | comb(k,2)*comb(n-k,2), #two dots and edge 102 | comb(n-k,4) + comb(n-k,3)*k]) # empty 103 | return (res/comb(n,4)) 104 | 105 | def create_graphlet_counts_array(self, graphlet_size, graph_size, clique_sizes): 106 | res = np.zeros((len(clique_sizes), 4)) 107 | if graphlet_size == 3: 108 | for i, c in enumerate(clique_sizes): 109 | res[i] = self.graphlets_counts_3(graph_size, c) 110 | else: 111 | for i, c in enumerate(clique_sizes): 112 | res[i] = self.graphlets_counts_4(graph_size, c) 113 | return res 114 | 115 | def create_single_graph_array(self, size, clique_sizes): 116 | res = np.zeros((len(clique_sizes), size, size)) 117 | for i, c in enumerate(clique_sizes): 118 | res[i] = self.clique_and_anti(size, c) 119 | return res 120 | 121 | def setUp(self): 122 | self.tol = 1e-7 123 | sizes3 = [[10, [4, 5, 6, 7]], 124 | [100, [5, 18, 29, 31, 56, 90]]] 125 | sizes4 = [[10, [4, 5, 6, 7]], 126 | [12, [5, 6, 7, 8, 9, 10]]] 127 | 128 | self.data3 = np.array([self.create_single_graph_array(s[0], s[1]) for s in sizes3]) 129 | self.data4 = np.array([self.create_single_graph_array(s[0], s[1]) for s in sizes4]) 130 | 131 | self.gr3 = np.array([self.create_graphlet_counts_array(3,s[0], s[1]) for s in sizes3]) 132 | self.gr4 = np.array([self.create_graphlet_counts_array(4,s[0], s[1]) for s in sizes4]) 133 | 134 | self.K3 = All34Graphlets(3) 135 | self.K4 = All34Graphlets(4) 136 | 137 | def tearDown(self): 138 | pass 139 | 140 | def testCount3Graphlets(self): 141 | graphlets = allgraphlets._generate_graphlets(3) 142 | for i, graph_array in enumerate(self.data3): 143 | for j, graph in enumerate(graph_array): 144 | self.assertTrue((allgraphlets._count_graphlets(graph, 3, graphlets)==self.gr3[i][j]).all()) 145 | 146 | def testCount4Graphlets(self): 147 | graphlets4 = allgraphlets._generate_graphlets(4) 148 | for i, graph_array in enumerate(self.data4): 149 | for j, graph in enumerate(graph_array): 150 | count = allgraphlets._count_graphlets(graph, 4, graphlets4) 151 | for g_num in self.gr4[i][j]: 152 | self.assertTrue((np.absolute(count-g_num) / (||x|| ||y||) 18 | 19 | """ 20 | 21 | def _compute(self, data_1, data_2): 22 | self._dim = data_1.shape[1] 23 | norm_1 = np.sqrt((data_1 ** 2).sum(axis=1)).reshape(data_1.shape[0], 1) 24 | norm_2 = np.sqrt((data_2 ** 2).sum(axis=1)).reshape(data_2.shape[0], 1) 25 | return data_1.dot(data_2.T) / (norm_1 * norm_2.T) 26 | 27 | def dim(self): 28 | return self._dim 29 | 30 | class Exponential(Kernel): 31 | """ 32 | Exponential kernel, 33 | 34 | K(x, y) = e^(-||x - y||/(2*s^2)) 35 | 36 | where: 37 | s = sigma 38 | """ 39 | 40 | def __init__(self, sigma=None): 41 | if sigma is None: 42 | self._sigma = None 43 | else: 44 | self._sigma = 2 * sigma**2 45 | 46 | def _compute(self, data_1, data_2): 47 | if self._sigma is None: 48 | # modification of libSVM heuristics 49 | self._sigma = float(data_1.shape[1]) 50 | 51 | dists_sq = euclidean_dist_matrix(data_1, data_2) 52 | return np.exp(-np.sqrt(dists_sq) / self._sigma) 53 | 54 | def dim(self): 55 | return np.inf 56 | 57 | 58 | class Laplacian(Exponential): 59 | """ 60 | Laplacian kernel, 61 | 62 | K(x, y) = e^(-||x - y||/s) 63 | 64 | where: 65 | s = sigma 66 | """ 67 | 68 | def __init__(self, sigma=None): 69 | self._sigma = sigma 70 | 71 | 72 | 73 | class RationalQuadratic(Kernel): 74 | """ 75 | Rational quadratic kernel, 76 | 77 | K(x, y) = 1 - ||x-y||^2/(||x-y||^2+c) 78 | 79 | where: 80 | c > 0 81 | """ 82 | 83 | def __init__(self, c=1): 84 | self._c = c 85 | 86 | def _compute(self, data_1, data_2): 87 | 88 | dists_sq = euclidean_dist_matrix(data_1, data_2) 89 | return 1. - (dists_sq / (dists_sq + self._c)) 90 | 91 | def dim(self): 92 | return None #unknown? 93 | 94 | 95 | class InverseMultiquadratic(Kernel): 96 | """ 97 | Inverse multiquadratic kernel, 98 | 99 | K(x, y) = 1 / sqrt(||x-y||^2 + c^2) 100 | 101 | where: 102 | c > 0 103 | 104 | as defined in: 105 | "Interpolation of scattered data: Distance matrices and conditionally positive definite functions" 106 | Charles Micchelli 107 | Constructive Approximation 108 | """ 109 | 110 | def __init__(self, c=1): 111 | self._c = c ** 2 112 | 113 | def _compute(self, data_1, data_2): 114 | 115 | dists_sq = euclidean_dist_matrix(data_1, data_2) 116 | return 1. / np.sqrt(dists_sq + self._c) 117 | 118 | def dim(self): 119 | return np.inf 120 | 121 | 122 | class Cauchy(Kernel): 123 | """ 124 | Cauchy kernel, 125 | 126 | K(x, y) = 1 / (1 + ||x - y||^2 / s ^ 2) 127 | 128 | where: 129 | s = sigma 130 | 131 | as defined in: 132 | "A least square kernel machine with box constraints" 133 | Jayanta Basak 134 | International Conference on Pattern Recognition 2008 135 | """ 136 | 137 | def __init__(self, sigma=None): 138 | if sigma is None: 139 | self._sigma = None 140 | else: 141 | self._sigma = sigma**2 142 | 143 | def _compute(self, data_1, data_2): 144 | if self._sigma is None: 145 | # modification of libSVM heuristics 146 | self._sigma = float(data_1.shape[1]) 147 | 148 | dists_sq = euclidean_dist_matrix(data_1, data_2) 149 | 150 | return 1 / (1 + dists_sq / self._sigma) 151 | 152 | def dim(self): 153 | return np.inf 154 | 155 | 156 | 157 | class TStudent(Kernel): 158 | """ 159 | T-Student kernel, 160 | 161 | K(x, y) = 1 / (1 + ||x - y||^d) 162 | 163 | where: 164 | d = degree 165 | 166 | as defined in: 167 | "Alternative Kernels for Image Recognition" 168 | Sabri Boughorbel, Jean-Philippe Tarel, Nozha Boujemaa 169 | INRIA - INRIA Activity Reports - RalyX 170 | http://ralyx.inria.fr/2004/Raweb/imedia/uid84.html 171 | """ 172 | 173 | def __init__(self, degree=2): 174 | self._d = degree 175 | 176 | def _compute(self, data_1, data_2): 177 | 178 | dists = np.sqrt(euclidean_dist_matrix(data_1, data_2)) 179 | return 1 / (1 + dists ** self._d) 180 | 181 | def dim(self): 182 | return None 183 | 184 | 185 | class ANOVA(Kernel): 186 | """ 187 | ANOVA kernel, 188 | K(x, y) = SUM_k exp( -sigma * (x_k - y_k)^2 )^d 189 | 190 | as defined in 191 | 192 | "Kernel methods in machine learning" 193 | Thomas Hofmann, Bernhard Scholkopf and Alexander J. Smola 194 | The Annals of Statistics 195 | http://www.kernel-machines.org/publications/pdfs/0701907.pdf 196 | """ 197 | 198 | def __init__(self, sigma=1., d=2): 199 | self._sigma = sigma 200 | self._d = d 201 | 202 | def _compute(self, data_1, data_2): 203 | 204 | kernel = np.zeros((data_1.shape[0], data_2.shape[0])) 205 | 206 | for d in range(data_1.shape[1]): 207 | column_1 = data_1[:, d].reshape(-1, 1) 208 | column_2 = data_2[:, d].reshape(-1, 1) 209 | kernel += np.exp( -self._sigma * (column_1 - column_2.T)**2 ) ** self._d 210 | 211 | return kernel 212 | 213 | def dim(self): 214 | return None 215 | 216 | 217 | def default_wavelet(x): 218 | return np.cos(1.75*x)*np.exp(-x**2/2) 219 | 220 | class Wavelet(Kernel): 221 | """ 222 | Wavelet kernel, 223 | 224 | K(x, y) = PROD_i h( (x_i-c)/a ) h( (y_i-c)/a ) 225 | 226 | or for c = None 227 | 228 | K(x, y) = PROD_i h( (x_i - y_i)/a ) 229 | 230 | as defined in 231 | "Wavelet Support Vector Machine" 232 | Li Zhang, Weida Zhou, Licheng Jiao 233 | IEEE Transactions on System, Man, and Cybernetics 234 | http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=1262479 235 | """ 236 | 237 | def __init__(self, h=default_wavelet, c=None, a=1): 238 | self._c = c 239 | self._a = a 240 | self._h = h 241 | 242 | def _compute(self, data_1, data_2): 243 | 244 | kernel = np.ones((data_1.shape[0], data_2.shape[0])) 245 | 246 | for d in range(data_1.shape[1]): 247 | column_1 = data_1[:, d].reshape(-1, 1) 248 | column_2 = data_2[:, d].reshape(-1, 1) 249 | if self._c is None: 250 | kernel *= self._h( (column_1 - column_2.T) / self._a ) 251 | else: 252 | kernel *= self._h( (column_1 - self._c) / self._a ) * self._h( (column_2.T - self._c) / self._a ) 253 | 254 | return kernel 255 | 256 | def dim(self): 257 | return None 258 | 259 | 260 | class Fourier(Kernel): 261 | """ 262 | Fourier kernel, 263 | 264 | K(x, y) = PROD_i (1-q^2)/(2(1-2q cos(x_i-y_i)+q^2)) 265 | """ 266 | 267 | def __init__(self, q=0.1): 268 | self._q = q 269 | 270 | def _compute(self, data_1, data_2): 271 | 272 | kernel = np.ones((data_1.shape[0], data_2.shape[0])) 273 | 274 | for d in range(data_1.shape[1]): 275 | column_1 = data_1[:, d].reshape(-1, 1) 276 | column_2 = data_2[:, d].reshape(-1, 1) 277 | kernel *= (1-self._q ** 2) / \ 278 | (2.*(1. - 2.*self._q *np.cos(column_1 - column_2.T) + self._q ** 2)) 279 | 280 | return kernel 281 | 282 | def dim(self): 283 | return None 284 | 285 | class Tanimoto(Kernel): 286 | """ 287 | Tanimoto kernel 288 | K(x, y) = / (||x||^2 + ||y||^2 - ) 289 | 290 | as defined in: 291 | 292 | "Graph Kernels for Chemical Informatics" 293 | Liva Ralaivola, Sanjay J. Swamidass, Hiroto Saigo and Pierre Baldi 294 | Neural Networks 295 | http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.92.483&rep=rep1&type=pdf 296 | """ 297 | def _compute(self, data_1, data_2): 298 | 299 | norm_1 = (data_1 ** 2).sum(axis=1).reshape(data_1.shape[0], 1) 300 | norm_2 = (data_2 ** 2).sum(axis=1).reshape(data_2.shape[0], 1) 301 | prod = data_1.dot(data_2.T) 302 | return prod / (norm_1 + norm_2.T - prod) 303 | 304 | def dim(self): 305 | return None 306 | 307 | 308 | class Sorensen(Kernel): 309 | """ 310 | Sorensen kernel 311 | K(x, y) = 2 / (||x||^2 + ||y||^2) 312 | 313 | as defined in: 314 | 315 | "Graph Kernels for Chemical Informatics" 316 | Liva Ralaivola, Sanjay J. Swamidass, Hiroto Saigo and Pierre Baldi 317 | Neural Networks 318 | http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.92.483&rep=rep1&type=pdf 319 | """ 320 | def _compute(self, data_1, data_2): 321 | 322 | norm_1 = (data_1 ** 2).sum(axis=1).reshape(data_1.shape[0], 1) 323 | norm_2 = (data_2 ** 2).sum(axis=1).reshape(data_2.shape[0], 1) 324 | prod = data_1.dot(data_2.T) 325 | return 2 * prod / (norm_1 + norm_2.T) 326 | 327 | def dim(self): 328 | return None 329 | 330 | from abc import ABCMeta 331 | 332 | class PositiveKernel(Kernel): 333 | """ 334 | Defines kernels which can be only used with positive values 335 | """ 336 | __metaclass__ = ABCMeta 337 | 338 | class AdditiveChi2(PositiveKernel): 339 | """ 340 | Additive Chi^2 kernel, 341 | K(x, y) = SUM_i 2 x_i y_i / (x_i + y_i) 342 | 343 | as defined in 344 | 345 | "Efficient Additive Kernels via Explicit Feature Maps" 346 | Andrea Vedaldi, Andrew Zisserman 347 | IEEE TRANSACTIONS ON PATTERN ANALYSIS AND MACHINE INTELLIGENCE 348 | http://www.robots.ox.ac.uk/~vedaldi/assets/pubs/vedaldi11efficient.pdf 349 | """ 350 | 351 | def _compute(self, data_1, data_2): 352 | 353 | if np.any(data_1 < 0) or np.any(data_2 < 0): 354 | warnings.warn('Additive Chi^2 kernel requires data to be strictly positive!') 355 | 356 | kernel = np.zeros((data_1.shape[0], data_2.shape[0])) 357 | 358 | for d in range(data_1.shape[1]): 359 | column_1 = data_1[:, d].reshape(-1, 1) 360 | column_2 = data_2[:, d].reshape(-1, 1) 361 | kernel += 2 * (column_1 * column_2.T) / (column_1 + column_2.T) 362 | 363 | return kernel 364 | 365 | def dim(self): 366 | return None 367 | 368 | class Chi2(PositiveKernel): 369 | """ 370 | Chi^2 kernel, 371 | K(x, y) = exp( -gamma * SUM_i (x_i - y_i)^2 / (x_i + y_i) ) 372 | 373 | as defined in: 374 | 375 | "Local features and kernels for classification 376 | of texture and object categories: A comprehensive study" 377 | Zhang, J. and Marszalek, M. and Lazebnik, S. and Schmid, C. 378 | International Journal of Computer Vision 2007 379 | http://eprints.pascal-network.org/archive/00002309/01/Zhang06-IJCV.pdf 380 | """ 381 | 382 | def __init__(self, gamma=1.): 383 | self._gamma = gamma 384 | 385 | def _compute(self, data_1, data_2): 386 | 387 | if np.any(data_1 < 0) or np.any(data_2 < 0): 388 | warnings.warn('Chi^2 kernel requires data to be strictly positive!') 389 | 390 | kernel = np.zeros((data_1.shape[0], data_2.shape[0])) 391 | 392 | for d in range(data_1.shape[1]): 393 | column_1 = data_1[:, d].reshape(-1, 1) 394 | column_2 = data_2[:, d].reshape(-1, 1) 395 | kernel += (column_1 - column_2.T)**2 / (column_1 + column_2.T) 396 | 397 | return np.exp(-self._gamma * kernel) 398 | 399 | def dim(self): 400 | return None 401 | 402 | class Min(PositiveKernel): 403 | """ 404 | Min kernel (also known as Histogram intersection kernel) 405 | K(x, y) = SUM_i min(x_i, y_i) 406 | 407 | """ 408 | 409 | def _compute(self, data_1, data_2): 410 | 411 | if np.any(data_1 < 0) or np.any(data_2 < 0): 412 | warnings.warn('Min kernel requires data to be strictly positive!') 413 | 414 | kernel = np.zeros((data_1.shape[0], data_2.shape[0])) 415 | 416 | for d in range(data_1.shape[1]): 417 | column_1 = data_1[:, d].reshape(-1, 1) 418 | column_2 = data_2[:, d].reshape(-1, 1) 419 | kernel += np.minimum(column_1, column_2.T) 420 | 421 | return kernel 422 | 423 | def dim(self): 424 | return None 425 | 426 | 427 | class GeneralizedHistogramIntersection(Kernel): 428 | """ 429 | Generalized histogram intersection kernel 430 | K(x, y) = SUM_i min(|x_i|^alpha, |y_i|^alpha) 431 | 432 | as defined in 433 | "Generalized histogram intersection kernel for image recognition" 434 | Sabri Boughorbel, Jean-Philippe Tarel, Nozha Boujemaa 435 | International Conference on Image Processing (ICIP-2005) 436 | http://perso.lcpc.fr/tarel.jean-philippe/publis/jpt-icip05.pdf 437 | """ 438 | 439 | def __init__(self, alpha=1.): 440 | self._alpha = alpha 441 | 442 | def _compute(self, data_1, data_2): 443 | 444 | return Min()._compute(np.abs(data_1)**self._alpha, 445 | np.abs(data_2)**self._alpha) 446 | 447 | def dim(self): 448 | return None 449 | 450 | class MinMax(PositiveKernel): 451 | """ 452 | MinMax kernel 453 | K(x, y) = SUM_i min(x_i, y_i) / SUM_i max(x_i, y_i) 454 | 455 | bounded by [0,1] as defined in: 456 | 457 | "Graph Kernels for Chemical Informatics" 458 | Liva Ralaivola, Sanjay J. Swamidass, Hiroto Saigo and Pierre Baldi 459 | Neural Networks 460 | http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.92.483&rep=rep1&type=pdf 461 | """ 462 | 463 | def _compute(self, data_1, data_2): 464 | 465 | if np.any(data_1 < 0) or np.any(data_2 < 0): 466 | warnings.warn('MinMax kernel requires data to be strictly positive!') 467 | 468 | minkernel = np.zeros((data_1.shape[0], data_2.shape[0])) 469 | maxkernel = np.zeros((data_1.shape[0], data_2.shape[0])) 470 | 471 | for d in range(data_1.shape[1]): 472 | column_1 = data_1[:, d].reshape(-1, 1) 473 | column_2 = data_2[:, d].reshape(-1, 1) 474 | minkernel += np.minimum(column_1, column_2.T) 475 | maxkernel += np.maximum(column_1, column_2.T) 476 | 477 | return minkernel/maxkernel 478 | 479 | def dim(self): 480 | return None 481 | 482 | 483 | class Spline(PositiveKernel): 484 | """ 485 | Spline kernel, 486 | K(x, y) = PROD_i 1 + x_iy_i + x_iy_i min(x_i,y_i) 487 | - (x_i+y_i)/2 * min(x_i,y_i)^2 488 | + 1/3 * min(x_i, y_i)^3 489 | 490 | as defined in 491 | 492 | "Support Vector Machines for Classification and Regression" 493 | Steve Gunn 494 | ISIS Technical Report 495 | http://www.svms.org/tutorials/Gunn1998.pdf 496 | """ 497 | 498 | def _compute(self, data_1, data_2): 499 | 500 | if np.any(data_1 < 0) or np.any(data_2 < 0): 501 | warnings.warn('Spline kernel requires data to be strictly positive!') 502 | 503 | kernel = np.ones((data_1.shape[0], data_2.shape[0])) 504 | 505 | for d in range(data_1.shape[1]): 506 | column_1 = data_1[:, d].reshape(-1, 1) 507 | column_2 = data_2[:, d].reshape(-1, 1) 508 | c_prod = column_1 * column_2.T 509 | c_sum = column_1 + column_2.T 510 | c_min = np.minimum(column_1, column_2.T) 511 | kernel *= 1. + c_prod + c_prod * c_min \ 512 | - c_sum/2. * c_min ** 2. \ 513 | + 1./3. * c_min ** 3. 514 | return kernel 515 | 516 | def dim(self): 517 | return None 518 | 519 | 520 | 521 | class ConditionalyPositiveDefiniteKernel(Kernel): 522 | """ 523 | Defines kernels which are only CPD 524 | """ 525 | __metaclass__ = ABCMeta 526 | 527 | class Log(ConditionalyPositiveDefiniteKernel): 528 | """ 529 | Log kernel 530 | K(x, y) = -log(||x-y||^d + 1) 531 | 532 | """ 533 | 534 | def __init__(self, d=2.): 535 | self._d = d 536 | 537 | def _compute(self, data_1, data_2): 538 | return -np.log(euclidean_dist_matrix(data_1, data_2) ** self._d / 2. + 1) 539 | 540 | def dim(self): 541 | return None 542 | 543 | 544 | class Power(ConditionalyPositiveDefiniteKernel): 545 | """ 546 | Power kernel 547 | K(x, y) = -||x-y||^d 548 | 549 | as defined in: 550 | "Scale-Invariance of Support Vector Machines based on the Triangular Kernel" 551 | Hichem Sahbi, Francois Fleuret 552 | Research report 553 | https://hal.inria.fr/inria-00071984 554 | """ 555 | 556 | def __init__(self, d=2.): 557 | self._d = d 558 | 559 | def _compute(self, data_1, data_2): 560 | return - euclidean_dist_matrix(data_1, data_2) ** self._d / 2. 561 | 562 | def dim(self): 563 | return None 564 | 565 | 566 | -------------------------------------------------------------------------------- /tests/data/random_100_node.csv: -------------------------------------------------------------------------------- 1 | 1,1,1,1,1,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1,0,0,1,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,1,1,0,1,1,1,0,1,0,0,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,0,1,1,0,0,0,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,0,0,1,0,0,0,1,1,1,0,0,1,1,0,0,1,1,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,1,1,1,1,0,1,1,1,0,1,1,0,0,0,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,1,1,0,1,1,0,0,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1,1,0,1,0,0,1,1,1,1,0,1,0,1,0,1,0,0,0,1,1,1,1,0,1,0,1,0,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,0,1,1,1,0,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,1,0,0,1,1,1,1,1,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,0,1,0,0,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,1,0,0,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1,0,1,1,0,1,0,1,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,0,0,0,0,1,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,1,1,1,0,1,0,1,1,0,0,0,1,1,0,0,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,0,0,0,1,1,0,1,0,0,1,0,1,1,1,1,1,0,0,1,0,0,1,0,1,1,1,0,0,0,0,1,1,1,1,1,0,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,0,0,1,1,0,0,1,1,1,0,0,1,0,1,0,0,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,1,1,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1,0,1,0,1,1,1,0,0,1,1,0,0,0,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,1,1,0,0,1,0,0,1,1,0,0,1,0,1,0,1,1,1,0,1,0,0,0,0,1,1,1,0,0,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,0,1,1,1,1,1,1,0,1,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,0,0,1,0,0,1,1,1,0,1,0,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0,1,0,0,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,0,1,0,1,0,0,1,0,1,0,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,1,0,1,0,1,1,0,1,1,1,0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,1,1,1,1,0,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,0,1,1,0,1,0,0,1,1,1,0,0,1,0,0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1,1,1,0,0,0,1,0,1,0,0,0,1,1,1,1,1,1,1,1,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,1,0,0,0,1,1,0,1,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,1,1,1,0,1,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,1,1,1,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,1,1,1,0,0,0,1,1,1,1,0,0,1,0,0,1,1,0,0,1,1,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,1,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1,1,1,0,0,1,1,0,1,1,1,0,0,1,0,1,0,0,0,1,1,0,1,0,0,1,1,0,1,0,1,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,0,1,0,0,0,0,0,0,0,0,1,0,1,1,0,1,1,1,0,1,1,0,0,1,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,1,1,1,0,1,0,0,0,0,1,1,1,0,0,1,1,0,1,0,1,1,1,0,1,1,0,1,0,0,1,0,1,0,0,0,0,0,1,1,1,1,0,1,0,1,1,1,1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,1,1,0,0,0,1,1,1,1,1,0,0,1,0,1,0,1,1,0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1,0,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,1,1,0,1,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,0,0,0,1,0,0,1,0,0,1,1,1,0,0,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0,1,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,1,0,1,1,0,1,0,0,1,1,0,1,0,1,0,1,1,1,1,1,1,1,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,0,1,1,1,1,1,0,0,0,1,0,1,1,0,1,0,0,0,1,1,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,0,1,1,1,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,1,1,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,0,1,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,0,0,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,1,1,0,0,0,1,0,1,1,0,0,1,1,1,1,0,0,1,0,1,0,1,0,0,0,1,1,1,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,1,0,1,1,1,1,0,0,1,1,0,1,0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,1,1,0,0,0,1,0,1,0,0,0,0,1,1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,1,1,0,1,1,1,0,1,0,1,0,0,1,1,1,0,1,0,1,1,0,1,0,1,0,1,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,1,0,1,0,1,1,1,1,0,1,0,1,1,0,1,1,1,1,0,1,0,1,1,1,0,0,0,1,1,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,0,1,0,0,1,1,0,1,1,0,1,1,0,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,0,0,1,0,1,0,0,1,0,1,0,0,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1,0,0,1,1,1,0,1,0,1,1,1,0,1,0,0,0,1,1,1,1,0,0,0,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,1,1,0,1,0,0,0,1,1,1,0,1,1,1,1,0,1,0,0,1,1,0,1,1,1,1,0,0,1,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,0,1,0,1,1,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,1,0,1,1,1,0,1,0,1,0,0,0,1,1,0,0,1,1,0,1,0,0,0,0,1,0,1,1,1,0,1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,0,1,0,0,0,0,1,1,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,1,0,1,0,1,0,1,0,1,0,0,1,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,1,1,1,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,0,1,0,0,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0,1,1,0,0,1,1,0,1,0,0,0,0,0,1,0,1,1,0,0,1,1,1,0,1,1,1,1,0,1,0,1,1,0,1,1,0,1,1,1,1,0,0,1,0,0,1,0,1,0,0,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,1,0,1,0,1,1,1,0,1,1,1,1,1,1,0,0,1,0,1,0,0,1,1,1,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,1,1,0,1,0,0,1,0,1,1,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0,1,0,0,0,0,1,0,1,1,0,1,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,0,1,0,0,1,0,1,1,0,1,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,1,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,1,1,0,1,1,1,0,1,1,0,1,1,1,0,1,0,1,0,0,1,1,0,1,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,0,1,0,0,0,0,0,1,1,0,0,0,1,1,1,1,0,1,0,1,1,0,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,0,1,1,0,0,1,0,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,1,0,0,1,1,0,1,0,0,0,0,1,1,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,0,1,1,0,0,1,0,0,0,1,0,1,0,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,1,0,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,0,1,0,1,1,1,1,1,0,1,1,0,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,1,1,1,1,0,0,1,0,0,0,0,0,1,1,0,1,0,1,0,1,0,1,1,0,0,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,1,0,1,0,0,1,1,0,0,1,1,1,0,1,1,1,0,1,1,0,0,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,1,1,1,0,1,0,1,0,0,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0,0,0,1,1,0,1,0,1,1,1,0,0,1,1,0,0,0,1,1,1,0,0,1,0,0,1,1,1,0,1,0,1,1,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,1,1,1,1,0,1,0,1,0,1,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,1,1,1,0,1,0,0,0,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,0,0,0,1,1,1,0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,0,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0,0,1,0,1,0,1,1,1,0,0,0,0,0,0,1,0,1,0,1,1,1,1,0,0,1,0,0,0,1,0,1,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0,1,0,1,1,1,0,1,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,1,0,1,1,0,0,1,1,0,0,1,1,1,0,0,1,0,1,1,1,1,0,0,0,1,0,0,0,1,1,0,1,1,1,1,1,0,0,1,1,0,0,1,0,1,0,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,0,1,0,0,1,0,1,0,1,1,1,0,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,1,1,0,1,1,0,1,1,1,1,0,1,1,0,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,0,0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1,1,1,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,0,1,0,1,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0,1,1,1,0,0,1,0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,1,1,0,0,0,0,1,0,0,1,1,1,1,0,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1,0,0,0,0,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1,0,0,1,0,0,0,0,0,1,1,1,0,1,0,1,1,1,1,0,1,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,0,1,0,1,1,1,0,1,1,0,0,0,0,1,0,0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,0,0,1,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,0,1,1,1,1,0,1,0,1,1,1,0,1,1,0,1,1,1,1,0,0,1,0,1,0,0,0,0,0,1,1,1,0,0,1,1,1,0,0,1,1,0,1,1,0,1,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,1,1,0,0,1,1,0,0,1,0,1,0,0,0,0,1,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,1,1,1,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,0,0,1,0,1,0,1,1,1,0,0,1,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,1,0,0,1,0,0,1,1,0,1,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,1,1,0,1,0,1,0,0,1,0,1,0,1,0,0,0,1,1,0,0,1,0,0,0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,1,1,1,1,0,1,1,1,0,0,1,1,0,0,0,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,1,0,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,1,0,0,1,1,1,0,1,1,0,0,1,1,1,0,1,0,1,1,1,0,1,0,0,1,0,1,1,0,1,1,0,0,0,1,0,0,0,0,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,1,1,0,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1,1,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,1,1,1,0,1,0,1,0,1,1,1,0,0,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,1,0,0,1,1,0,0,0,0,0,1,1,0,0,1,1,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,1,0,1,0,1,1,0,0,0,1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,1,0,1,0,0,0,1,1,1,0,0,1,1,0,1,0,1,0,0,0,0,1,0,1,0,0,1,1,1,1,0,1,1,1,0,0,0,1,1,1,1,0,0,1,0,1,0,0,1,1,0,0,0,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,0,0,0,0,1,0,1,1,0,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0,1,0,0,1,1,1,0,1,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,1,0,1,1,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1,0,1,1,1,0,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,0,1,1,0,0,1,1,0,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1,0,1,0,0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,0,1,1,0,1,1,0,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,1,0,1,0,0,1,1,1,1,1,1,0,1,1,1,0,0,1,0,1,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,1,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,0,0,1,1,0,1,1,0,1,0,1,0,0,0,1,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,1,1,1,0,0,1,0,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,0,0,1,1,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,1,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,1,1,0,0,0,0,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,1,0,1,1,0,1,1,1,1,0,0,0,1,1,0,1,1,0,0,1,1,0,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,1,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,1,1,1,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,1,1,0,1,1,0,0,1,0,0,0,1,1,1,0,1,0,1,1,1,1,1,1,1,0,0,1,1,1,0,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,0,1,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,1,1,1,1,0,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,1,1,0,0,1,0,0,0,0,1,0,1,1,0,1,0,1,1,1,0,0,1,1,1,1,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,0,0,0,1,1,0,1,1,1,1,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,1,1,0,1,0,1,0,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,0,0,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,1,0,1,0,0,0,0,1,0,0,1,1,1,1,0,1,0,0,1,1,1,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,0,0,1,0,0,0,1,0,1,1,1,0,0,0,0,1,1,0,1,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,0,1,0,1,0,0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,1,0,1,1,1,1,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,1,1,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,0,0,0,1,0,1,0,0,1,1,1,0,0,0,0,1,0,1,0,1,1,0,0,0,1,1,1,0,1,0,0,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,1,1,1,0,0,1,0,1,1,1,1,1,0,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,1,0,1,1,0,1,1,0,0,0,0,0,0,1,0,1,0,0,1,0,1,1,1,1,0,0,0,0,1,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,1,0,1,0,0,0,0,1,0,1,1,1,1,0,0,1,0,0,1,0,1,1,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,0,0,1,1,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,1,1,1,1,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,1,0,1,1,1,0,1,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,0,0,0,0,1,0,0,1,0,1,0,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,0,1,1,1,0,0,0,1,1,0,1,0,1,0,0,1,1,0,1,0,0,1,1,1,0,1,0,0,1,0,1,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,1,1,0,1,1,1,1,0,0,1,0,1,0,1,1,0,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,0,1,1,0,0,0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,1,0,0,1,1,1,1,0,1,1,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,0,0,1,1,0,1,0,1,1,0,1,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,1,1,1,0,1,0,0,1,0,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,0,1,0,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,0,0,0,1,1,0,1,0,0,1,1,0,0,1,1,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,1,1,1,0,0,0,0,1,0,1,1,0,0,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,0,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,0,0,1,1,1,1,0,1,0,1,1,0,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,1,1,0,1,0,1,0,1,1,1,1,0,1,0,0,1,1,0,0,0,0,1,0,1,1,0,1,0,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,0,1,0,1,1,1,1,0,1,1,1,0,0,1,0,0,1,1,0,0,1,0,0,1,1,0,0,0,1,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,0,1,1,1,0,0,0,1,1,1,0,0,1,1,0,1,1,1,0,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,1,0,1,1,0,1,0,1,0,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,1,0,1,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,1,0,1,1,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,1,0,0,1,0,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,0,1,1,0,1,0,0,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,1,1,1,0,1,1,0,1,0,1,1,1,0,0,1,0,0,0,1,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,1,0,1,0,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,1,1,1,1,1,1,1,0,1,0,0,1,0,0,0,0,1,1,1,1,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,0,1,1,0,1,0,0,1,0,1,1,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,0,0,1,0,1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,1,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,0,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,0,1,0,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,1,0,1,1,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0,1,1,1,1,1,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,1,1,0,1,1,1,0,0,1,0,1,1,0,1,1,0,1,0,1,1,0,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,1,1,0,1,0,0,1,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,1,0,1,1,1,0,1,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,0,1,1,0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,0,1,0,0,1,0,1,1,1,0,1,1,0,0,1,0,1,0,1,1,0,1,0,1,0,0,0,0,1,1,1,0,1,0,1,0,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,1,1,0,1,0,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,1,1,0,1,0,0,0,0,1,0,0,0,1,1,1,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,1,0,0,1,1,1,0,0,1,1,0,1,1,0,0,1,0,1,1,1,1,1,0,1,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,0,1,1,0,0,1,1,0,1,1,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,0,0,0,1,1,0,1,0,1,1,0,1,0,1,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,1,1,1,0,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,0,0,1,1,0,0,0,0,0,0,1,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,0,1,0,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,1,1,1,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,1,1,0,0,0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,0,0,0,0,1,0,1,1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,1,1,0,1,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,1,1,1,1,0,1,1,0,0,1,0,0,1,1,1,0,1,0,0,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,1,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,1,1,1,1,0,1,0,1,0,1,1,0,0,0,1,1,1,0,1,1,0,1,0,1,1,1,0,0,1,1,1,1,1,1,0,0,0,1,0,1,1,1,0,0,1,1,0,1,1,1,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,1,1,1,1,0,1,0,0,1,1,1,1,0,1,0,0,1,0,1,0,0,0,0,1,1,1,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,1,0,1,0,0,1,1,1,0,0,1,1,0,0,0,1,1,0,0,1,0,0,1,1,1,1,1,1,0,1,0,1,0,0,1,0,1,1,0,0,0,1,0,0,1,0,0,0,1,0,1,1,1,0,0,0,0,1,1,0,1,0,1,1,1,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,1,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,0,0,0,1,1,0,1,1,1,1,1,0,0,0,0,1,0,1,0,1,0,1,1,0,1,0,0,0,1,1,1,0,1,1,1,0,0,0,0,1,0,1,1,0,1,0,1,0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,0,1,0,0,1,1,1,0,1,0,1,1,1,0,1,1,0,0,1,0,0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,1,0,1,1,0,1,0,1,1,0,0,1,0,0,0,1,0,0,1,1,0,1,1,1,1,0,0,0,1,0,0,1,1,1,1,0,1,0,0,0,0,1,0,1,1,0,0,0,1,0,1,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1,1,1,1,0,0,1,0,0,0,0,1,1,1,1,0,0,0,1,1,0,1,0,1,1,1,0,0,1,1,0,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,0,0,1,0,1,1 --------------------------------------------------------------------------------