├── setup.cfg ├── MANIFEST.in ├── countrings ├── __init__.py ├── __main__.py ├── countrings.py └── countrings_nx.py ├── imgs ├── sample1.png ├── sample2.png └── sample3.png ├── countrings.x ├── .gitignore ├── samples ├── Makefile ├── cube.ngph └── test.ngph ├── c ├── Bond.h ├── SparseMatrix_CountRings.h ├── Makefile ├── Int64Hash.h ├── CountRings.h ├── SparseMatrix.h ├── Int64Hash.c ├── countrings2.c ├── SparseMatrix_CountRings.c ├── SparseMatrix.c └── CountRings.c ├── util ├── replace.py └── crossingrings.pl ├── Makefile ├── setup.py ├── temp_README.md └── README.md /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include setup.cfg 3 | -------------------------------------------------------------------------------- /countrings/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = "0.1.7" 3 | -------------------------------------------------------------------------------- /imgs/sample1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitroid/CountRings/HEAD/imgs/sample1.png -------------------------------------------------------------------------------- /imgs/sample2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitroid/CountRings/HEAD/imgs/sample2.png -------------------------------------------------------------------------------- /imgs/sample3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitroid/CountRings/HEAD/imgs/sample3.png -------------------------------------------------------------------------------- /countrings.x: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from countrings import __main__ 4 | __main__.main() 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | dist/* 3 | *.egg-info/* 4 | *.pyc 5 | *.rst 6 | samples/*rngs 7 | c/countrings2 8 | -------------------------------------------------------------------------------- /samples/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ls *.ngph | sed -e s/ngph/rngs/ | xargs make 3 | 4 | %.rngs: %.ngph 5 | ../countrings.x < $< > $@ 6 | -------------------------------------------------------------------------------- /samples/cube.ngph: -------------------------------------------------------------------------------- 1 | @NGPH 2 | 8 3 | 0 1 4 | 1 2 5 | 2 3 6 | 3 0 7 | 4 5 8 | 5 6 9 | 6 7 10 | 7 4 11 | 0 4 12 | 1 5 13 | 2 6 14 | 3 7 15 | -1 -1 16 | -------------------------------------------------------------------------------- /c/Bond.h: -------------------------------------------------------------------------------- 1 | #ifndef _BOND_H 2 | #define _BOND_H 3 | 4 | #define MAXBOND 12 5 | typedef struct { 6 | int n; 7 | int to[MAXBOND]; 8 | char flag[MAXBOND]; /* このプログラムでは使わない。 */ 9 | } BondType; 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /c/SparseMatrix_CountRings.h: -------------------------------------------------------------------------------- 1 | #ifndef _SPARSEMATRIX_COUNTRINGS_H 2 | #define _SPARSEMATRIX_COUNTRINGS_H 3 | #include "SparseMatrix.h" 4 | #include "CountRings.h" 5 | 6 | /*in SparseMatrix_CountRings.c*/ 7 | int CountRings2(RingType *ring,int max,BondType *bond,int bond_n,sSparseMatrix *path,int maxsize); 8 | int SetBonds(sSparseMatrix *path,BondType *bond); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /c/Makefile: -------------------------------------------------------------------------------- 1 | #include ../Makefile.settings 2 | BINTARGET=countrings2 3 | TARGET=$(BINTARGET) 4 | OBJS=countrings2.o SparseMatrix.o Int64Hash.o CountRings.o SparseMatrix_CountRings.o 5 | CFLAGS=-Werror 6 | 7 | all: $(BINTARGET) 8 | countrings2: $(OBJS) 9 | $(CC) $^ -o countrings2 $(LDFLAGS) 10 | clean: 11 | -rm $(OBJS) 12 | archive: clean 13 | cd ..; tar zcvf CountRings.tar.gz CountRings/* 14 | -------------------------------------------------------------------------------- /util/replace.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # utility command 4 | 5 | from __future__ import print_function 6 | import sys 7 | import string 8 | tmp = sys.argv[1] 9 | by = sys.stdin.readlines() 10 | sep = sys.argv[2] 11 | by = sep.join(by) 12 | by = sep+by 13 | orig = open(sys.argv[3]).readlines() 14 | orig = "".join(orig) 15 | orig = orig.replace(tmp, by) 16 | print(orig, end="") 17 | 18 | -------------------------------------------------------------------------------- /c/Int64Hash.h: -------------------------------------------------------------------------------- 1 | #ifndef _Int64Hash_H 2 | #define _Int64Hash_H 3 | 4 | typedef struct 5 | { 6 | int nentry; 7 | u_int64_t hashsize; 8 | int shift; 9 | u_int64_t *key; 10 | int *value; 11 | } 12 | sInt64Hash; 13 | 14 | int Int64Hash_RegisterValue(sInt64Hash *ih, u_int64_t key,int value); 15 | int Int64Hash_QueryValue(sInt64Hash *ih, u_int64_t key); 16 | void Int64Hash_EraseOne(sInt64Hash *ih, u_int64_t key); 17 | sInt64Hash *Int64Hash_Init(int size); 18 | void Int64Hash_Done(sInt64Hash *ih); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /samples/test.ngph: -------------------------------------------------------------------------------- 1 | @NGPH 2 | 37 3 | 33 34 4 | 21 22 5 | 21 26 6 | 7 8 7 | 7 10 8 | 26 27 9 | 26 29 10 | 2 3 11 | 2 10 12 | 17 18 13 | 17 31 14 | 1 6 15 | 1 2 16 | 18 19 17 | 30 34 18 | 16 17 19 | 16 20 20 | 27 30 21 | 25 30 22 | 28 36 23 | 28 29 24 | 20 21 25 | 20 31 26 | 14 22 27 | 14 19 28 | 14 15 29 | 24 25 30 | 24 36 31 | 10 13 32 | 31 32 33 | 35 36 34 | 11 28 35 | 11 12 36 | 22 25 37 | 0 1 38 | 0 5 39 | 13 33 40 | 23 32 41 | 23 24 42 | 29 32 43 | 6 7 44 | 3 11 45 | 3 4 46 | 9 33 47 | 12 27 48 | 12 13 49 | 15 16 50 | 8 9 51 | 4 35 52 | 4 5 53 | 34 35 54 | 19 23 55 | 5 9 56 | -1 -1 57 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKGNAME=CountRings 2 | all: 3 | echo Hello. 4 | %: temp_% 5 | countrings -h | python3 util/replace.py %%usage%% " " $< > $@ 6 | 7 | test-deploy: build 8 | twine upload -r pypitest dist/* 9 | test-install: 10 | pip3 install --index-url https://test.pypi.org/simple/ $(PKGNAME) 11 | 12 | install: check 13 | ./setup.py install 14 | uninstall: 15 | pip uninstall $(PKGNAME) 16 | build: README.md 17 | ./setup.py sdist bdist_wheel 18 | deploy: build 19 | twine upload dist/* 20 | check: 21 | ./setup.py check 22 | distclean: 23 | -rm -rf build dist 24 | -rm README.rst 25 | -rm .DS_Store 26 | find . -name __pycache__ | xargs rm -rf 27 | find . -name \*.pyc | xargs rm -rf 28 | find . -name \*~ | xargs rm -rf 29 | -------------------------------------------------------------------------------- /c/CountRings.h: -------------------------------------------------------------------------------- 1 | #ifndef _COUNTRINGS_H 2 | #define _COUNTRINGS_H 3 | #include "Bond.h" 4 | 5 | void HeapError(void); 6 | 7 | #define MAXRINGSIZE 13 8 | typedef struct { 9 | int n; 10 | int list[MAXRINGSIZE]; 11 | } RingType; 12 | 13 | int ReadBonds(BondType *bond,int max,FILE *fp); 14 | int CheckBonds(BondType *bond,int bond_n); 15 | void MinPath(int **path,BondType *bond,int bond_n); 16 | int CountRings(RingType *ring,int max,BondType *bond,int bond_n,int **path, int maxRingSize ); 17 | int SimplifyRings(RingType *ring,int ring_n); 18 | int RingCompare(const void *e1,const void *e2); 19 | 20 | #define NMAX 4097 21 | #define RINGMAX 1800000 22 | 23 | #define _crmin(x,y) (((x)<(y))?(x):(y)) 24 | 25 | void _insertsort(int n,int *a); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /c/SparseMatrix.h: -------------------------------------------------------------------------------- 1 | #ifndef _SPARSEMATRIX_H 2 | #define _SPARSEMATRIX_H 3 | #include "Int64Hash.h" 4 | 5 | /* 2!3! = 12重にcountするはず。*/ 6 | 7 | typedef struct 8 | { 9 | int nadj; 10 | int *adj; 11 | } 12 | sLineElement; 13 | 14 | typedef struct 15 | { 16 | /*size of the matrix*/ 17 | int nline,ncolumn,maxadj; 18 | sLineElement *e_line,*e_column; 19 | sInt64Hash *ih; 20 | } 21 | sSparseMatrix; 22 | 23 | void SparseMatrix_RegisterValue(sSparseMatrix *path,int i,int j,int value); 24 | int SparseMatrix_QueryValue(sSparseMatrix *path,int i,int j); 25 | sSparseMatrix *SparseMatrix_Init(int nline,int ncolumn,int hashbit,int maxadj); 26 | void SparseMatrix_Done(sSparseMatrix *path); 27 | sSparseMatrix *SparseMatrix_LoadSSMX(FILE *fp,int hashbit,int maxadj); 28 | sSparseMatrix *SparseMatrix_LoadNGPH(FILE *fp,int hashbit,int maxadj); 29 | sSparseMatrix *SparseMatrix_LoadAsymNGPH(FILE *fp,int hashbit,int maxadj); 30 | sSparseMatrix *SparseMatrix_LoadGRPH(FILE *fp,int hashbit,int maxadj); 31 | sSparseMatrix *SparseMatrix_LoadAsymGRPH(FILE *fp,int hashbit,int maxadj); 32 | void SparseMatrix_SaveSMTX(FILE *fp,sSparseMatrix *path); 33 | /*compatibility for older version*/ 34 | #define SparseMatrix_RegisterBasicElement(a,b,c,d) SparseMatrix_RegisterValue(a,b,c,d) 35 | 36 | 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from setuptools import setup 4 | import os 5 | import codecs 6 | import re 7 | 8 | #Copied from wheel package 9 | here = os.path.abspath(os.path.dirname(__file__)) 10 | #README = codecs.open(os.path.join(here, 'README.txt'), encoding='utf8').read() 11 | #CHANGES = codecs.open(os.path.join(here, 'CHANGES.txt'), encoding='utf8').read() 12 | 13 | with codecs.open(os.path.join(os.path.dirname(__file__), 'countrings', '__init__.py'), 14 | encoding='utf8') as version_file: 15 | metadata = dict(re.findall(r"""__([a-z]+)__ = "([^"]+)""", version_file.read())) 16 | 17 | setup(name='CountRings', 18 | version=metadata['version'], 19 | description='Count rings in a graph.', 20 | #long_description=README + '\n\n' + CHANGES, 21 | classifiers=[ 22 | "Development Status :: 4 - Beta", 23 | "Intended Audience :: Developers", 24 | "Programming Language :: Python", 25 | "Programming Language :: Python :: 3.5", 26 | ], 27 | author='Masakazu Matsumoto', 28 | author_email='vitroid@gmail.com', 29 | url='https://github.com/vitroid/CountRings/', 30 | keywords=['countrings',], 31 | license='MIT', 32 | packages=['countrings', 33 | ], 34 | install_requires=['networkx', 'methodtools', 'numpy'], 35 | entry_points = { 36 | 'console_scripts': [ 37 | 'countrings = countrings.__main__:main' 38 | ] 39 | } 40 | ) 41 | 42 | -------------------------------------------------------------------------------- /c/Int64Hash.c: -------------------------------------------------------------------------------- 1 | /*整数(0を含まない)を要素とするHash。*/ 2 | 3 | #include 4 | #include 5 | #include "Int64Hash.h" 6 | #define EMPTY -1 7 | 8 | /*shiftとxorによる簡単なhash keyの生成*/ 9 | u_int64_t _Int64Hash_Encode(sInt64Hash *s, u_int64_t key) 10 | { 11 | u_int64_t mod=0; 12 | while(key) 13 | { 14 | mod ^= (key & (s->hashsize-1)); 15 | key >>= s->shift; 16 | } 17 | return mod; 18 | } 19 | 20 | /*hash要素の番号を返す。もし存在しない要素なら、空き要素を返す。要素は追加されない。*/ 21 | u_int64_t _Int64Hash_QueryElement(sInt64Hash *ih, u_int64_t key) 22 | { 23 | u_int64_t e=_Int64Hash_Encode(ih,key); 24 | while(1){ 25 | if(ih->key[e]==EMPTY){ 26 | ih->value[e]=0; 27 | return e; 28 | } 29 | if(ih->key[e]==key){ 30 | return e; 31 | } 32 | e+=13; 33 | if(e>=ih->hashsize) 34 | e-=ih->hashsize; 35 | } 36 | } 37 | 38 | /*値を登録する。更新なら0、追加なら1を返す。*/ 39 | int Int64Hash_RegisterValue(sInt64Hash *ih, u_int64_t key,int value) 40 | { 41 | u_int64_t e=_Int64Hash_QueryElement(ih,key); 42 | int v=ih->value[e]; 43 | if(v==0){ 44 | ih->key[e]=key; 45 | ih->nentry++; 46 | if(ih->nentry > ih->hashsize/2){ 47 | fprintf(stderr,"Warning: hash size seems too small.\n"); 48 | } 49 | if(ih->nentry >= ih->hashsize){ 50 | fprintf(stderr,"Error: hash overflow.\n"); 51 | exit(1); 52 | } 53 | } 54 | ih->value[e]=value; 55 | return (v==0); 56 | } 57 | 58 | /*値を参照する。*/ 59 | int Int64Hash_QueryValue(sInt64Hash *ih,u_int64_t key) 60 | { 61 | return ih->value[_Int64Hash_QueryElement(ih,key)]; 62 | } 63 | 64 | /*値を抹消する。*/ 65 | void Int64Hash_EraseOne(sInt64Hash *ih,u_int64_t key) 66 | { 67 | ih->key[_Int64Hash_QueryElement(ih,key)]=EMPTY; 68 | } 69 | 70 | sInt64Hash *Int64Hash_Init(int size) 71 | { 72 | sInt64Hash *ih=malloc(sizeof(sInt64Hash)); 73 | int i; 74 | u_int64_t m; 75 | 76 | m=1<shift=size; 78 | ih->hashsize=m; 79 | ih->key=malloc(m*sizeof(u_int64_t)); 80 | ih->value=calloc(m,sizeof(int)); 81 | ih->nentry=0; 82 | for(i=0;ikey[i]=EMPTY; 84 | return ih; 85 | } 86 | 87 | void Int64Hash_Done(sInt64Hash *ih) 88 | { 89 | free(ih->key); 90 | free(ih->value); 91 | free(ih); 92 | } 93 | -------------------------------------------------------------------------------- /countrings/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from countrings import countrings_nx as cr 4 | import sys 5 | import argparse as ap 6 | import logging 7 | 8 | 9 | 10 | def getoptions(): 11 | parser = ap.ArgumentParser(description='') 12 | parser.add_argument('--count', '-c', action='store_true', dest='countonly', 13 | help='Only output the ring statistics.') 14 | parser.add_argument('maxsize', nargs='?', default=8, type=int, 15 | help='Maximum size of the ringd') 16 | 17 | parser.add_argument('--debug', '-D', action='store_true', dest='debug', 18 | help='Output debugging info.') 19 | parser.add_argument('--quiet', '-q', action='store_true', dest='quiet', 20 | help='Do not output progress messages.') 21 | return parser.parse_args() 22 | 23 | 24 | def main(): 25 | options = getoptions() 26 | if options.debug: 27 | logging.basicConfig(level=logging.DEBUG, 28 | format="%(asctime)s %(levelname)s %(message)s") 29 | elif options.quiet: 30 | logging.basicConfig(level=logging.WARN, 31 | format="%(asctime)s %(levelname)s %(message)s") 32 | else: 33 | #normal 34 | logging.basicConfig(level=logging.INFO, 35 | format="%(asctime)s %(levelname)s %(message)s") 36 | logger = logging.getLogger() 37 | logger.debug("Debug mode.") 38 | logger.debug("Max size: {0}".format(options.maxsize)) 39 | logger.debug("Stat only: {0}".format(options.countonly)) 40 | 41 | 42 | file = sys.stdin 43 | while True: 44 | line = file.readline() 45 | if not line: 46 | break 47 | if line[0:5] == "@NGPH": 48 | (nmol,network) = cr.readNGPH(file) 49 | #print shortest_path(network, 0,3) 50 | ri = cr.CountRings(network).rings_iter( options.maxsize ) 51 | if options.countonly: 52 | count = [0] * (options.maxsize-2) 53 | for ring in ri: 54 | count[len(ring)-3]+=1 55 | print (" ".join([str(x) for x in count]) ) 56 | else: 57 | print (cr.saveRNGS( nmol, ri ),end="") 58 | 59 | if __name__ == "__main__": 60 | main() 61 | -------------------------------------------------------------------------------- /util/crossingrings.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # -*- coding: utf-8 -*- 3 | # 4 | #Looking up the crossing rings. 5 | # 6 | use strict; 7 | 8 | my $debug = ( $ARGV[0] eq "-d" ); 9 | 10 | while(){ 11 | if ( /^\@RNGS/ ){ 12 | if ( ! $debug ){ 13 | print; 14 | } 15 | my $nnode = ; 16 | if ( ! $debug ){ 17 | print $nnode; 18 | } 19 | my @ringsatthenode; 20 | my @nodesatthering; 21 | my $nring=0; 22 | while(){ 23 | chomp; 24 | my ( @nodes ) = split; 25 | last if ( shift @nodes ) <= 0; 26 | $nodesatthering[$nring] = [ @nodes ]; 27 | foreach my $node ( @nodes ){ 28 | push @{$ringsatthenode[$node]}, $nring; 29 | } 30 | $nring ++; 31 | } 32 | # 33 | #隣接する環が共有する節点の表を作る。 34 | # 35 | my @ringring; 36 | foreach my $node ( 0..$#ringsatthenode ){ 37 | if ( $ringsatthenode[$node] ){ 38 | my @rings = @{$ringsatthenode[$node]}; 39 | for(my $i=0; $i<=$#rings; $i++ ){ 40 | my $ii = $rings[$i]; 41 | for(my $j=$i+1; $j<=$#rings; $j++ ){ 42 | my $jj = $rings[$j]; 43 | push @{$ringring[$ii][$jj]}, $node; 44 | push @{$ringring[$jj][$ii]}, $node; 45 | } 46 | } 47 | } 48 | } 49 | my @omit; 50 | foreach my $i ( 0..$#ringring ){ 51 | if ( ! $omit[$i] ){ 52 | my @nodes = @{$nodesatthering[$i]}; 53 | foreach my $j ( 0.. $#{$ringring[$i]} ){ 54 | if ( $i < $j ){ 55 | if ( ! $omit[$j] && defined $ringring[$i][$j]){ 56 | my @shared = @{$ringring[$i][$j]}; 57 | next if $#shared<0; 58 | #print join( " ", $i, $j, @shared ), "\n"; 59 | #print join(":", @nodes), " nodes\n"; 60 | my @mark; 61 | foreach my $s ( @shared ){ 62 | $mark[$s] ++; 63 | } 64 | my $block=0; 65 | my $isone=(0<$mark[$nodes[$#nodes]]); 66 | foreach my $node ( @nodes ){ 67 | #立ち上がり 68 | if ( ! $isone && $mark[$node] ){ 69 | $block++; 70 | $isone = 1; 71 | } 72 | #立ち下がり 73 | if ( $isone && ! $mark[$node] ){ 74 | $isone = 0; 75 | } 76 | } 77 | #立ち上がりが2回以上あれば、交差がおきている。 78 | if ( 1 < $block ){ 79 | if ( $debug ){ 80 | print "$i $j\n"; 81 | print join(":", @nodes), " nodes\n"; 82 | print join(":", @{$nodesatthering[$j]}), " nodes\n"; 83 | print join(":", @{$ringring[$i][$j]}), " shared\n"; 84 | } 85 | else{ 86 | print STDERR "[" . ($#{$nodesatthering[$j]}+1) . "]"; 87 | } 88 | $omit[$j] = 1; 89 | } 90 | } 91 | } 92 | } 93 | if ( ! $debug ){ 94 | print join( " ", $#nodes+1, @nodes ), "\n"; 95 | } 96 | } 97 | } 98 | if ( ! $debug ){ 99 | print "0\n"; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /countrings/countrings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | #To confirm that countrings2 is working correctly. 3 | #I doubted the bug in countrings2, but seems alright (See README). 4 | 5 | #Ok, I confirmed that the results of 2 and 3 are the same. 6 | #Use countrings2 because this is slower. 7 | 8 | import heapq 9 | import logging 10 | from collections import defaultdict 11 | 12 | 13 | #http://code.activestate.com/recipes/119466/ 14 | def shortest_path(G, start, end): 15 | 16 | q = [(0, start, [])] # Heap of (cost, path_head, path_rest). 17 | visited = set() # Visited vertices. 18 | while True: 19 | (cost, v1, path) = heapq.heappop(q) 20 | if v1 not in visited: 21 | visited.add(v1) 22 | if v1 == end: 23 | return path + [v1] 24 | path.append(v1) 25 | for v2 in G[v1]: 26 | if v2 not in visited: 27 | heapq.heappush(q, (cost + 1, v2, path)) 28 | 29 | 30 | def readNGPH(file): 31 | line = file.readline() 32 | #print line, 33 | n = int(line) 34 | network = defaultdict(set) 35 | while True: 36 | line = file.readline() 37 | xyz = line.split() 38 | #print xyz 39 | i,j = map(int,xyz[:2]) 40 | if i < 0: 41 | return (n,network) 42 | network[i].add(j) 43 | network[j].add(i) 44 | 45 | 46 | def shortcuts( network, members ): 47 | n = len(members) 48 | for i in range(0,n): 49 | for j in range(i+1,n): 50 | d = min(j-i, n-(j-i)) 51 | path = len(shortest_path(network, members[i],members[j]))-1 52 | if path < d: 53 | return True 54 | return False 55 | 56 | 57 | def findring( network, members, max ): 58 | #print members, "MAX:", max 59 | if len(members) > max: 60 | return (max, []) 61 | s = set(members) 62 | last = members[-1] 63 | results = [] 64 | for adj in network[last]: 65 | if adj in s: 66 | if adj == members[0]: 67 | #Ring is closed. 68 | #It is the best and unique answer. 69 | if not shortcuts( network, members ): 70 | return (len(members), [members]) 71 | else: 72 | #Shortcut ring 73 | pass 74 | else: 75 | (newmax,newres) = findring( network,members + [adj], max ) 76 | if newmax < max: 77 | max = newmax 78 | results = newres 79 | elif newmax == max: 80 | results += newres 81 | return (max, results) 82 | 83 | 84 | def rings_iter( network, maxsize ): 85 | logger = logging.getLogger() 86 | rings = set() 87 | for x in network.keys(): 88 | neis = network[x] 89 | if neis != None: 90 | for y in neis: 91 | for z in neis: 92 | if y < z: 93 | members = [y,x,z] 94 | (max, results) = findring( network, members, maxsize ) 95 | for i in results: 96 | #Make i immutable for the key. 97 | j = frozenset(i) 98 | #and original list as the value. 99 | if j not in rings: 100 | logger.debug("({0}) {1}".format(len(i),i)) 101 | yield i 102 | rings.add(j) 103 | 104 | 105 | def totalrings( network, maxsize ): 106 | logger = logging.getLogger() 107 | logger.info("totalrings() is outdated. Use rings_iter.") 108 | rings = dict() 109 | for ring in rings_iter( network, maxsize ): 110 | s = frozenset(ring) 111 | rings[s] = ring 112 | return rings 113 | 114 | 115 | def saveRNGS( nmol, ri ): #ri is a rings_iter 116 | s = "@RNGS\n" 117 | s += "%d\n" % nmol 118 | for ring in ri: 119 | s+= "%s " % len(ring) + " ".join( map(str,ring) ) + "\n" 120 | s += "0\n" 121 | return s 122 | 123 | 124 | if __name__ == "__main__": 125 | edges = {"A": ("B","D"), 126 | "B": ("C","D","E"), 127 | "C": ("E",), 128 | "D": ("E","F"), 129 | "E": ("F","G"), 130 | "F": ("G",)} 131 | 132 | print(edges) 133 | print("A -> E:") 134 | print(shortest_path(edges, "A", "E")) 135 | print("F -> G:") 136 | print(shortest_path(edges, "F", "G")) 137 | -------------------------------------------------------------------------------- /countrings/countrings_nx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | #To confirm that countrings2 is working correctly. 3 | #I doubted the bug in countrings2, but seems alright (See README). 4 | 5 | #Ok, I confirmed that the results of 2 and 3 are the same. 6 | #Use countrings2 because this is slower. 7 | 8 | import logging 9 | import networkx as nx 10 | import itertools 11 | from methodtools import lru_cache 12 | import numpy as np 13 | 14 | 15 | def readNGPH(file): 16 | logger = logging.getLogger() 17 | line = file.readline() 18 | #print line, 19 | n = int(line) 20 | network = nx.Graph() #undirected 21 | logger.debug("Uses NetworkX") 22 | while True: 23 | line = file.readline() 24 | xyz = line.split() 25 | #print xyz 26 | i,j = map(int,xyz[:2]) 27 | if i < 0: 28 | return (n,network) 29 | network.add_edge(i,j) 30 | 31 | 32 | 33 | # Make a class to cache the pair distance dict 34 | class CountRings(nx.Graph): 35 | dist = dict() 36 | def __init__(self, network, pos=None): 37 | super(CountRings, self).__init__(network) 38 | self.network = network 39 | self.pos = pos # fractional coordinate in a orthogonal cell in numpy array 40 | 41 | 42 | #shortes_pathlen is a stateless function, so the cache is useful to avoid re-calculations. 43 | @lru_cache(maxsize=None) 44 | def shortest_pathlen(self, pair): 45 | return len(nx.shortest_path(self.network, *pair)) - 1 46 | 47 | 48 | def shortcuts( self, members ): 49 | n = len(members) 50 | for i in range(0,n): 51 | for j in range(i+1,n): 52 | d = min(j-i, n-(j-i)) 53 | if d > self.shortest_pathlen(frozenset((members[i],members[j]))): 54 | return True 55 | return False 56 | 57 | 58 | def findring( self, members, max ): 59 | #print members, "MAX:", max 60 | if len(members) > max: 61 | return (max, []) 62 | s = set(members) 63 | last = members[-1] 64 | results = [] 65 | for adj in self[last]: 66 | if adj in s: 67 | if adj == members[0]: 68 | #Ring is closed. 69 | #It is the best and unique answer. 70 | if not self.shortcuts( members ): 71 | return (len(members), [members]) 72 | else: 73 | #Shortcut ring 74 | pass 75 | else: 76 | (newmax,newres) = self.findring( members + [adj], max ) 77 | if newmax < max: 78 | max = newmax 79 | results = newres 80 | elif newmax == max: 81 | results += newres 82 | return (max, results) 83 | 84 | 85 | def rings_iter( self, maxsize ): 86 | logger = logging.getLogger() 87 | rings = set() 88 | for x in self: 89 | neis = sorted(self[x]) 90 | for y,z in itertools.combinations(neis, 2): 91 | triplet = [y,x,z] 92 | (max, results) = self.findring( triplet, maxsize ) 93 | for i in results: 94 | #Make i immutable for the key. 95 | j = frozenset(i) 96 | #and original list as the value. 97 | if j not in rings: 98 | # logger.debug("({0}) {1}".format(len(i),i)) 99 | if self.pos is None or not self.is_spanning(i): 100 | yield i 101 | rings.add(j) 102 | 103 | 104 | def is_spanning(self, cycle): 105 | sum = np.zeros_like(self.pos[cycle[0]]) 106 | for i in range(len(cycle)): 107 | d = self.pos[cycle[i-1]] - self.pos[cycle[i]] 108 | d -= np.floor(d+0.5) 109 | sum += d 110 | return np.any(np.absolute(sum) > 1e-5) 111 | 112 | 113 | 114 | def saveRNGS( nmol, ri ): #ri is a rings_iter 115 | s = "@RNGS\n" 116 | s += "%d\n" % nmol 117 | for ring in ri: 118 | s+= "%s " % len(ring) + " ".join( map(str,ring) ) + "\n" 119 | s += "0\n" 120 | return s 121 | 122 | 123 | 124 | if __name__ == "__main__": 125 | file = open("../samples/test.ngph") 126 | while True: 127 | line = file.readline() 128 | if 0 == line.find("@NGPH"): 129 | n, network = readNGPH(file) 130 | break 131 | print(saveRNGS( n, CountRings(network).rings_iter(8) )) 132 | 133 | -------------------------------------------------------------------------------- /temp_README.md: -------------------------------------------------------------------------------- 1 | # Ring Statistics Algorithm 2 | 3 | ## Counting policy 4 | 5 | * It counts only irreducible rings (rings not having shortcut bridges). 6 | * It counts rings purely topologically. It does not use geometrical information. 7 | * Edge direction is not considered. (Undirected graph) 8 | 9 | ## Algorithm 10 | 11 | 1. Choose 3 successive nodes (i.e. two adjacent acyclic edges) along the network. (King's criteria) [King1991] 12 | 1. Find the smallest rings passing the three nodes. 13 | 1. The ring must not have shotcuts, i.e. path connecting two vertices on the ring which is shorter than the path along the ring. (Using Dijkstra's algorithm.) (Franzblau's SP ring criteria) [Franzblau1991] 14 | 1. Put the ring in the list. 15 | 1. Repeat 1 .. 4 until all sets of 3 successive nodes are tested. 16 | 1. Eliminate the permutations of a ring in the list. 17 | 1. (Optional) Remove "crossing rings". 18 | 19 | So, our definition is a hybrid of the algorithms of King and Franzblau. 20 | 21 | ### Note 22 | 23 | * Our definition is different from Franzblau's SP ring. Our algorithm does not count the 6-membered rings in a cubic graph but counts the geodesic 4-membered rings in a regular octahedral graph. [Franzblau1991] 24 | * Our definition is different from King's K ring. [King1991] 25 | * Our definition is different from Goetzke's strong ring. We do not care the strength. [Goetzke1991] 26 | * Our definition is different from that of Camisasca's. They count too much 5-membered rings. [Camisasca2019] 27 | * Probably somebody has already made the same definition. Let me know if you find that. 28 | 29 | ## Usage 30 | 31 | %%usage%% 32 | 33 | Input data must be in @NGPH format. Output data will be in @RNGS format. 34 | 35 | % ./countrings.py 8 < test.ngph > test.rngs 36 | % ./countrings.py 8 < test.ngph | ./crossingrings.pl > test2.rngs 37 | 38 | ## Sample 39 | 40 | The following is the expression of a cubic graph, which should have six 4-cycles. 41 | 42 | @NGPH 43 | 8 44 | 0 1 45 | 1 2 46 | 2 3 47 | 3 0 48 | 4 5 49 | 5 6 50 | 6 7 51 | 7 4 52 | 0 4 53 | 1 5 54 | 2 6 55 | 3 7 56 | -1 -1 57 | 58 | You can pick up many samples at the Vitrite database: 59 | http://reg.chem.okayama-u.ac.jp/cgi-bin/vitrite.cgi 60 | 61 | ## Known Problems 62 | 63 | ### Sample 1 64 | 65 |
66 | Number of rings in this kind of graph consisting of N bows is counted as N (N-1) / 2. It happens because of the lack of 3-dimentional geometrical information. 67 | 68 | ### Sample 2 69 | 70 |
71 | It is a smaller version of sample 1 consisting of 4 bows. As you see, surface rings of this structure seems to be 4, while the algorithm counts as 6, because it also counts the “crossing rings” (diagonal red and blue rings). These sample topologies rarely appear in the network of water at low temperature because the z-index at the top and bottom nodes is too large. 72 | While, there is another sample containing crossing rings but still undistorted. 73 | 74 | ### Sample 3 75 | 76 | 77 | 78 | ## Wayarounds 79 | 80 | The small Perl program “crossingrings.pl” looks up all the crossing rings in the ring list (in @RNGS format) and remove one of them randomly until the crossing is avoided. With the use of 3-dimentional geometrical information, there might be better walkarounds. 81 | 82 | ## Note 83 | 84 | This program is developed for analysing the hydrogen bond network of water. 85 | An affordable and relevant definition of the hydogen bonds is discussed here. 86 | 87 | ## To Cite It 88 | 89 | * M. Matsumoto, A. Baba, and I. Ohmine, Topological building blocks of hydrogen bond network in water, J. Chem. Phys. 127, 134504 (2007); [doi:10.1063/1.2772627](http://dx.doi.org/doi:10.1063/1.2772627) 90 | 91 | ## References 92 | 93 | * Camisasca, G., Schlesinger, D., Zhovtobriukh, I., Pitsevich, G. & Pettersson, L. G. M. A proposal for the structure of high- and low-density fluctuations in liquid water. J. Chem. Phys. 151, 034508 (2019). 94 | * Downs, G. M., Gillet, V. J., Holliday, J. D. & Lynch, M. F. Review of ring perception algorithms for chemical graphs. J. Chem. Inf. Comput. Sci. 29, 172–187 (1989). 95 | * Franzblau, D. S. Computation of ring statistics for network models of solids. Phys. Rev. B 44, 4925–4930 (1991). 96 | * Goetzke, K. & Klein, H. J. Properties and efficient algorithmic determination of different classes of rings in finite and infinite polyhedral networks. J. Non-Cryst. Solids. 127, 215–220 (1991). 97 | * KING, S. V. Ring Configurations in a Random Network Model of Vitreous Silica. Nature 213, 1112–1113 (1967). 98 | * Marians, C. S. & Hobbs, L. W. Network properties of crystalline polymorphs of silica. J. Non-Cryst. Solids. 124, 242–253 (1990). 99 | -------------------------------------------------------------------------------- /c/countrings2.c: -------------------------------------------------------------------------------- 1 | /* ネットワークの員環構造を求めるプログラム Ver. 0.70 */ 2 | /*$Id: countrings2.c,v 1.2 2007/09/20 09:45:39 matto Exp $ 3 | $Log: countrings2.c,v $ 4 | Revision 1.2 2007/09/20 09:45:39 matto 5 | Version down. varray is removed because of miscounting the 8 membered rings in CountRings/test.ngph 6 | 7 | Revision 1.5 2005-11-09 16:01:05 matto 8 | *** empty log message *** 9 | 10 | Revision 1.4 2004/01/27 08:07:09 matto 11 | IntHash is replaced by Int64Hash. 12 | 13 | Revision 1.3 2003/12/02 02:43:29 matto 14 | 15 | * Revision 1.2 2000/07/19 04:38:30 matto 16 | * countrings2 is modified to count rings at a time.(@RNGS format) 17 | * 18 | Revision 1.1.1.1 2000/02/25 03:01:57 matto 19 | Count rings in the network and find out order parameters. 20 | 21 | 22 | 巨大なシステムに対応できるように松本が変更を行った。 23 | 平成11年4月9日(金)IntHashとSparseMatrixを使用して簡素化しよう。 24 | 25 | 平成11年7月22日(木)逆に、小さな系で6員環が一周してしまう問題が 26 | 残っている。環は座標に関係なくトポロジーだけで定義されるものであり、 27 | 座標による制約は外部プログラムで行ったほうがすっきりすると思う。*/ 28 | #include 29 | #include 30 | #include 31 | #include "SparseMatrix_CountRings.h" 32 | 33 | void usage(char *cmd) 34 | { 35 | fprintf(stderr,"$Id: countrings2.c,v 1.2 2007/09/20 09:45:39 matto Exp $\n"); 36 | fprintf(stderr,"%s: List the rings in graph.\n",cmd); 37 | fprintf(stderr,"usage: %s -c maxsize < inputfile\n",cmd); 38 | fprintf(stderr,"usage: %s -C maxsize < inputfile\n",cmd); 39 | fprintf(stderr,"usage: %s size < inputfile\n",cmd); 40 | fprintf(stderr," inputfile must be in @NGPH or @GRPH format\n"); 41 | fprintf(stderr,"Option:\n"); 42 | fprintf(stderr," -c\tOutput the number of rings only.\n"); 43 | fprintf(stderr,"Example:\n"); 44 | fprintf(stderr," %s -c 6\tPrint the number of rings upto 6.\n",cmd); 45 | fprintf(stderr," %s -C 6\tPrint the number of rings upto 6 in @RNGS format.\n",cmd); 46 | fprintf(stderr," %s 3 5 7\tPrint the list of 3-, 5-, and 7-membered rings in @RNGL format\n",cmd); 47 | exit(1); 48 | } 49 | 50 | int main(int argc,char *argv[]) { 51 | RingType *ring; 52 | int ring_n; 53 | int i,j; 54 | char buf[255]; 55 | int maxsize,output[MAXRINGSIZE],count=0,rngs=0; 56 | 57 | /*command line parser*/ 58 | for(i=0;i=MAXRINGSIZE) 81 | usage(argv[0]); 82 | output[j]++; 83 | if(j>maxsize) 84 | maxsize=j; 85 | } 86 | } 87 | 88 | /*memory allocation*/ 89 | if ((ring = (RingType *) malloc(sizeof(RingType)*RINGMAX)) == NULL) 90 | HeapError(); 91 | 92 | while(NULL!=fgets(buf,sizeof(buf),stdin)){ 93 | sSparseMatrix *path=NULL; 94 | if(0==strncmp(buf,"@NGPH",5)){ 95 | /*maxadjは水の場合100で十分。hashbitはlog2(100*粒子数*2) 最 96 | 大で20ぐらいは必要かも。*/ 97 | path=SparseMatrix_LoadNGPH(stdin,24,1000); 98 | }else if(0==strncmp(buf,"@GRPH",5)){ 99 | /*maxadjは水の場合100で十分。hashbitはlog2(100*粒子数*2) 最 100 | 大で20ぐらいは必要かも。*/ 101 | path=SparseMatrix_LoadGRPH(stdin,24,1000); 102 | } 103 | if(path!=NULL){ 104 | int bond_n; 105 | BondType *bond; 106 | /*BondType情報は、SparseMatrixから抽出する。*/ 107 | if ((bond = (BondType *) malloc(sizeof(BondType)*path->nline)) == NULL) 108 | HeapError(); 109 | bond_n=SetBonds(path,bond); 110 | 111 | if (!CheckBonds(bond,bond_n)) 112 | {fprintf(stderr,"Data error!\n"); exit(1);} 113 | 114 | /* 員環構造の探索(3点固定、重複あり) */ 115 | ring_n = CountRings2(ring,RINGMAX,bond,bond_n,path,maxsize); 116 | 117 | /* 重複の除去 */ 118 | ring_n = SimplifyRings(ring,ring_n); 119 | 120 | /* 出力 */ 121 | if(count){ 122 | for(i=3;i<=maxsize;i++){ 123 | output[i]=0; 124 | } 125 | for (i = 0; i < ring_n; i++){ 126 | if(ring[i].n<=maxsize) 127 | output[ring[i].n]++; 128 | } 129 | for(i=3;i<=maxsize;i++){ 130 | printf("%d ",output[i]); 131 | } 132 | printf("\n"); 133 | }else if(rngs){ 134 | int s; 135 | printf("@RNGS\n%d\n",path->nline); 136 | for (i = 0; i < ring_n; i++){ 137 | int j; 138 | if(ring[i].n<=maxsize){ 139 | printf("%d ",ring[i].n); 140 | for(j=0;jnline); 152 | for (i = 0; i < ring_n; i++){ 153 | if(ring[i].n==s){ 154 | int j; 155 | for(j=0;j@NGPH format. Output data will be in @RNGS format. 59 | 60 | % ./countrings.py 8 < test.ngph > test.rngs 61 | % ./countrings.py 8 < test.ngph | ./crossingrings.pl > test2.rngs 62 | 63 | ## Sample 64 | 65 | The following is the expression of a cubic graph, which should have six 4-cycles. 66 | 67 | @NGPH 68 | 8 69 | 0 1 70 | 1 2 71 | 2 3 72 | 3 0 73 | 4 5 74 | 5 6 75 | 6 7 76 | 7 4 77 | 0 4 78 | 1 5 79 | 2 6 80 | 3 7 81 | -1 -1 82 | 83 | You can pick up many samples at the Vitrite database: 84 | http://reg.chem.okayama-u.ac.jp/cgi-bin/vitrite.cgi 85 | 86 | ## Known Problems 87 | 88 | ### Sample 1 89 | 90 |
91 | Number of rings in this kind of graph consisting of N bows is counted as N (N-1) / 2. It happens because of the lack of 3-dimentional geometrical information. 92 | 93 | ### Sample 2 94 | 95 |
96 | It is a smaller version of sample 1 consisting of 4 bows. As you see, surface rings of this structure seems to be 4, while the algorithm counts as 6, because it also counts the “crossing rings” (diagonal red and blue rings). These sample topologies rarely appear in the network of water at low temperature because the z-index at the top and bottom nodes is too large. 97 | While, there is another sample containing crossing rings but still undistorted. 98 | 99 | ### Sample 3 100 | 101 | 102 | 103 | ## Wayarounds 104 | 105 | The small Perl program “crossingrings.pl” looks up all the crossing rings in the ring list (in @RNGS format) and remove one of them randomly until the crossing is avoided. With the use of 3-dimentional geometrical information, there might be better walkarounds. 106 | 107 | ## Note 108 | 109 | This program is developed for analysing the hydrogen bond network of water. 110 | An affordable and relevant definition of the hydogen bonds is discussed here. 111 | 112 | ## To Cite It 113 | 114 | * M. Matsumoto, A. Baba, and I. Ohmine, Topological building blocks of hydrogen bond network in water, J. Chem. Phys. 127, 134504 (2007); [doi:10.1063/1.2772627](http://dx.doi.org/doi:10.1063/1.2772627) 115 | 116 | ## References 117 | 118 | * Camisasca, G., Schlesinger, D., Zhovtobriukh, I., Pitsevich, G. & Pettersson, L. G. M. A proposal for the structure of high- and low-density fluctuations in liquid water. J. Chem. Phys. 151, 034508 (2019). 119 | * Downs, G. M., Gillet, V. J., Holliday, J. D. & Lynch, M. F. Review of ring perception algorithms for chemical graphs. J. Chem. Inf. Comput. Sci. 29, 172–187 (1989). 120 | * Franzblau, D. S. Computation of ring statistics for network models of solids. Phys. Rev. B 44, 4925–4930 (1991). 121 | * Goetzke, K. & Klein, H. J. Properties and efficient algorithmic determination of different classes of rings in finite and infinite polyhedral networks. J. Non-Cryst. Solids. 127, 215–220 (1991). 122 | * KING, S. V. Ring Configurations in a Random Network Model of Vitreous Silica. Nature 213, 1112–1113 (1967). 123 | * Marians, C. S. & Hobbs, L. W. Network properties of crystalline polymorphs of silica. J. Non-Cryst. Solids. 124, 242–253 (1990). 124 | -------------------------------------------------------------------------------- /c/SparseMatrix_CountRings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "SparseMatrix.h" 4 | #include "CountRings.h" 5 | 6 | extern int _crbuf_p[MAXRINGSIZE]; /* 粒子番号表 */ 7 | extern int _crbuf_b[MAXRINGSIZE]; /* 結合番号表 */ 8 | void MinPath2(sSparseMatrix *path,BondType *bond,int bond_n,int maxpath) { 9 | int work[NMAX]; 10 | int head,tail; 11 | int i,j,k,to,length; 12 | 13 | /* 初期化 */ 14 | /*for (i = 0; i < bond_n; i++) 15 | for (j = 0; j < bond_n; j++) 16 | path[i][j] = bond_n; 未計算の意味 */ 17 | 18 | for (i = 0; i < bond_n; i++) { 19 | //fprintf(stderr,"%d\r",i); 20 | for(j=0;j head) { 26 | k = work[head++]; 27 | length = SparseMatrix_QueryValue(path,i,k)+1; 28 | /*printf("---[%d %d]=%d(%d %d)\n",i,k,length,bond[0].n,tail);*/ 29 | for (j = 0; j < bond[k].n; j++) { 30 | int value; 31 | to = bond[k].to[j]; 32 | if(i!=to){ 33 | value=SparseMatrix_QueryValue(path,i,to); 34 | /*printf("(%d %d)=%d\n",i,to,value);*/ 35 | if (value==0 && length <= maxpath) { 36 | /* 新規登録 */ 37 | work[tail++] = to; 38 | SparseMatrix_RegisterValue(path,i,to,length); 39 | }else if (value > length) 40 | SparseMatrix_RegisterValue(path,i,to,length); 41 | } 42 | } 43 | 44 | } 45 | } 46 | } 47 | 48 | int _CountRings2(RingType *ring,int max,BondType *bond,sSparseMatrix *path) { 49 | int ring_n = 0; 50 | int n,k,flag; 51 | int i,j; 52 | 53 | flag = 1; 54 | for (n = 4; n <= MAXRINGSIZE && ring_n <= 0 && flag; n++) { 55 | 56 | /* n員環の探索 */ 57 | _crbuf_b[2] = 0; 58 | i = 3; 59 | flag = 0; 60 | while (i >= 3) { 61 | if (i >= n) { 62 | flag = 1; 63 | if (SparseMatrix_QueryValue(path,_crbuf_p[0],_crbuf_p[n-1]) == 1) { 64 | /* n員環を発見、登録 */ 65 | if (ring_n < max) { 66 | ring[ring_n].n = n; 67 | k = 0; 68 | for (j = 1; j < n; j++) 69 | if (_crbuf_p[j] < _crbuf_p[k]) 70 | k = j; 71 | if (_crbuf_p[(k-1+n)%n] > _crbuf_p[(k+1)%n]) { 72 | for (j = 0; j < n; j++) 73 | ring[ring_n].list[j] = _crbuf_p[(k+j)%n]; 74 | } else { 75 | for (j = 0; j < n; j++) 76 | ring[ring_n].list[j] = _crbuf_p[(k-j+n)%n]; 77 | } 78 | ring_n++; 79 | } else 80 | return -1; /* 登録不能、異常終了 */ 81 | } 82 | } else { 83 | if (_crbuf_b[i-1] < bond[_crbuf_p[i-1]].n) { 84 | _crbuf_p[i] = bond[_crbuf_p[i-1]].to[_crbuf_b[i-1]]; 85 | /* 粒子間距離の判定 */ 86 | /*p = path[_crbuf_p[i]];*/ 87 | for (j = 0; j < i-1 88 | && SparseMatrix_QueryValue(path,_crbuf_p[i],_crbuf_p[j]) >= _crmin(i-j,n-i+j); j++) 89 | ; 90 | if (j >= i-1) { 91 | /* 採択。1つ下がる */ 92 | _crbuf_b[i++] = 0; 93 | } else { 94 | /* 棄却。次を試す */ 95 | _crbuf_b[i-1]++; 96 | } 97 | continue; 98 | } 99 | } 100 | /* 1つ上がる */ 101 | i--; 102 | _crbuf_b[i-1]++; /* 最後に_crbuf_b[1]++をしてしまうが問題なし */ 103 | } 104 | } 105 | 106 | return ring_n; 107 | } 108 | 109 | /* 全員環数の数え上げ(返値は員環の総数、異常は負を返す。) */ 110 | int CountRings2(RingType *ring,int max,BondType *bond,int bond_n,sSparseMatrix *path,int maxsize) { 111 | int p0,p1,p2,b0,b1,b2; 112 | int ring_n = 0; 113 | int ret; 114 | int i,j; 115 | 116 | /* 各粒子間の最小距離を求める */ 117 | MinPath2(path,bond,bond_n,maxsize/2); 118 | /*for(i=0;i= p2) continue; 133 | if (SparseMatrix_QueryValue(path,p0,p2) == 1) { 134 | /* 3員環発見、登録 */ 135 | if (p0 < p1 && p1 < p2) { 136 | if (ring_n < max) { 137 | ring[ring_n].n = 3; 138 | ring[ring_n].list[0] = p0; 139 | ring[ring_n].list[1] = p1; 140 | ring[ring_n].list[2] = p2; 141 | ring_n++; 142 | } else 143 | return -1; /* 登録不能、異常終了 */ 144 | } 145 | } else { 146 | /* n員環探索(n>3) */ 147 | _crbuf_p[0] = p0; 148 | _crbuf_p[1] = p1; 149 | _crbuf_p[2] = p2; 150 | _crbuf_b[0] = b0; 151 | _crbuf_b[1] = b1; 152 | _crbuf_b[2] = 0; 153 | ret = _CountRings2(ring+ring_n,max-ring_n,bond,path); 154 | if (ret >= 0) 155 | ring_n += ret; 156 | else 157 | return -1; /* 登録不能、異常終了 */ 158 | } 159 | } 160 | } 161 | } 162 | 163 | return ring_n; 164 | } 165 | 166 | int SetBonds(sSparseMatrix *path,BondType *bond) { 167 | int i,j; 168 | 169 | /* 初期化 */ 170 | 171 | for (i = 0; i < path->nline; i++) 172 | bond[i].n = 0; 173 | 174 | /* 読み込み */ 175 | for(i=0;inline;i++){ 176 | int n=bond[i].n=path->e_line[i].nadj; 177 | for(j=0;je_line[i].adj[j]; 179 | } 180 | } 181 | 182 | /* ソート */ 183 | for (i = 0; i < path->nline; i++) 184 | _insertsort(bond[i].n, bond[i].to); 185 | 186 | return path->nline; 187 | } 188 | 189 | -------------------------------------------------------------------------------- /c/SparseMatrix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SparseMatrix.c 3 | * 4 | * Treating sparse matrices with hash table 5 | * $Id: SparseMatrix.c,v 1.2 2007/09/20 09:45:39 matto Exp $ 6 | * $Log: SparseMatrix.c,v $ 7 | * Revision 1.2 2007/09/20 09:45:39 matto 8 | * Version down. varray is removed because of miscounting the 8 membered rings in CountRings/test.ngph 9 | * 10 | * Revision 1.2 2004-01-27 08:07:09 matto 11 | * IntHash is replaced by Int64Hash. 12 | * 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include "SparseMatrix.h" 18 | 19 | /*要素(i,j)にvalueを登録する。valueが0の場合、本来ならHashからデータを消し、隣 20 | 接情報からも除かなければいけないが、実際的な必要が生じていないので、 21 | ここではvalueが0のケースは考えていない。*/ 22 | void SparseMatrix_RegisterValue(sSparseMatrix *path,int i,int j,int value) 23 | { 24 | int k; 25 | int new=Int64Hash_RegisterValue(path->ih,(u_int64_t)i*(u_int64_t)path->ncolumn+(u_int64_t)j,value); 26 | if(new){ 27 | if(path->e_line[i].nadj>=path->maxadj-1){ 28 | fprintf(stderr,"Too many elements in line %d\n",i); 29 | exit(1); 30 | } 31 | path->e_line[i].adj[path->e_line[i].nadj++]=j; 32 | if(path->e_column[j].nadj>=path->maxadj-1){ 33 | fprintf(stderr,"Too many elements in column %d\n",i); 34 | exit(1); 35 | } 36 | path->e_column[j].adj[path->e_column[j].nadj++]=i; 37 | } 38 | } 39 | 40 | 41 | /*要素(i,j)の値を返す。*/ 42 | int SparseMatrix_QueryValue(sSparseMatrix *path,int i,int j) 43 | { 44 | return Int64Hash_QueryValue(path->ih,(u_int64_t)i*(u_int64_t)path->ncolumn+(u_int64_t)j); 45 | } 46 | 47 | void _sp_hasherror() 48 | { 49 | fprintf(stderr,"Hash error. Halted.\n"); 50 | exit(1); 51 | } 52 | 53 | 54 | /*空疎対称行列の初期化*/ 55 | sSparseMatrix *SparseMatrix_Init(int nline,int ncolumn,int hashbit,int maxadj) 56 | { 57 | int i; 58 | sSparseMatrix *path=malloc(sizeof(sSparseMatrix)); 59 | path->nline=nline; 60 | path->ncolumn=ncolumn; 61 | path->maxadj=maxadj; 62 | path->ih=Int64Hash_Init(hashbit); 63 | if(NULL==(path->e_line=calloc(nline,sizeof(sLineElement)))) 64 | _sp_hasherror(); 65 | if(NULL==(path->e_column=calloc(ncolumn,sizeof(sLineElement)))) 66 | _sp_hasherror(); 67 | for(i=0;ie_line[i].adj=malloc(sizeof(int)*maxadj))) 69 | _sp_hasherror(); 70 | } 71 | for(i=0;ie_column[i].adj=malloc(sizeof(int)*maxadj))) 73 | _sp_hasherror(); 74 | } 75 | return path; 76 | } 77 | 78 | /*空疎対称行列のdestructor*/ 79 | void SparseMatrix_Done(sSparseMatrix *path) 80 | { 81 | int i; 82 | for(i=0;inline;i++){ 83 | free(path->e_line[i].adj); 84 | } 85 | for(i=0;incolumn;i++){ 86 | free(path->e_column[i].adj); 87 | } 88 | free(path->e_line); 89 | free(path->e_column); 90 | Int64Hash_Done(path->ih); 91 | free(path); 92 | } 93 | 94 | /* 空疎対称行列の読み込み */ 95 | sSparseMatrix *SparseMatrix_LoadSSMX(FILE *fp,int hashbit,int maxadj) { 96 | int n; 97 | char buf[256]; 98 | sSparseMatrix *path; 99 | /*SSMX形式を想定。1行目は節点の数*/ 100 | fgets(buf,sizeof(buf),fp); 101 | n=atoi(buf); 102 | /*初期化*/ 103 | path=SparseMatrix_Init(n,n,hashbit,maxadj); 104 | /*隣接関係の読みこみ。*/ 105 | while(NULL!=fgets(buf,sizeof(buf),fp)){ 106 | int i,j,v; 107 | sscanf(buf,"%d %d %d",&i,&j,&v); 108 | /**/ 109 | if(i<0)break; 110 | /*無向グラフとしてあつかう。*/ 111 | SparseMatrix_RegisterValue(path,i,j,v); 112 | if(i!=j) 113 | SparseMatrix_RegisterValue(path,j,i,v); 114 | } 115 | return path; 116 | } 117 | 118 | /* 二値空疎対称行列の読み込み */ 119 | sSparseMatrix *SparseMatrix_LoadNGPH(FILE *fp,int hashbit,int maxadj) { 120 | int n; 121 | char buf[256]; 122 | sSparseMatrix *path; 123 | /*NGPH形式を想定。1行目は節点の数*/ 124 | fgets(buf,sizeof(buf),fp); 125 | n=atoi(buf); 126 | /*初期化*/ 127 | path=SparseMatrix_Init(n,n,hashbit,maxadj); 128 | /*隣接関係の読みこみ。*/ 129 | while(NULL!=fgets(buf,sizeof(buf),fp)){ 130 | int i,j,v; 131 | sscanf(buf,"%d %d",&i,&j); 132 | /**/ 133 | if(i<0)break; 134 | /*無向グラフとしてあつかう。*/ 135 | SparseMatrix_RegisterValue(path,i,j,1); 136 | if(i!=j) 137 | SparseMatrix_RegisterValue(path,j,i,1); 138 | } 139 | return path; 140 | } 141 | 142 | /* 二値空疎非対称行列の読み込み */ 143 | sSparseMatrix *SparseMatrix_LoadAsymNGPH(FILE *fp,int hashbit,int maxadj) { 144 | int n; 145 | char buf[256]; 146 | sSparseMatrix *path; 147 | /*NGPH形式を想定。1行目は節点の数*/ 148 | fgets(buf,sizeof(buf),fp); 149 | n=atoi(buf); 150 | /*初期化*/ 151 | path=SparseMatrix_Init(n,n,hashbit,maxadj); 152 | /*隣接関係の読みこみ。*/ 153 | while(NULL!=fgets(buf,sizeof(buf),fp)){ 154 | int i,j,v; 155 | sscanf(buf,"%d %d",&i,&j); 156 | /**/ 157 | if(i<0)break; 158 | /*無向グラフとしてあつかう。*/ 159 | SparseMatrix_RegisterValue(path,i,j,1); 160 | } 161 | return path; 162 | } 163 | 164 | /* 二値対称行列の読み込み */ 165 | sSparseMatrix *SparseMatrix_LoadGRPH(FILE *fp,int hashbit,int maxadj) { 166 | int n,i,j; 167 | char buf[10000]; 168 | sSparseMatrix *path; 169 | /*GRPH形式を想定。1行目は節点の数*/ 170 | fgets(buf,sizeof(buf),fp); 171 | n=atoi(buf); 172 | /*初期化*/ 173 | path=SparseMatrix_Init(n,n,hashbit,maxadj); 174 | /*隣接関係の読みこみ。*/ 175 | for(i=0;inline!=path->ncolumn){ 220 | fprintf(stderr,"Warning: Not a square matrix.\n"); 221 | } 222 | fprintf(fp,"@SMTX\n%d\n",path->nline); 223 | for(i=0;inline;i++){ 224 | for(j=0;jncolumn;j++){ 225 | fprintf(fp,"%d ",SparseMatrix_QueryValue(path,i,j)); 226 | } 227 | fprintf(fp,"\n"); 228 | } 229 | } 230 | 231 | void SparseMatrix_SaveNGPH(FILE *fp,sSparseMatrix *path) { 232 | int n; 233 | char buf[256]; 234 | int i,j; 235 | if(path->nline!=path->ncolumn){ 236 | fprintf(stderr,"Warning: Not a square matrix.\n"); 237 | } 238 | fprintf(fp,"@NGPH\n%d\n",path->nline); 239 | for(i=0;inline;i++){ 240 | for(j=0;je_line[i].nadj;j++){ 241 | int k=path->e_line[i].adj[j]; 242 | int v=SparseMatrix_QueryValue(path,i,k); 243 | if(v>1){ 244 | fprintf(stderr,"Warning: not a binary value %d at element %d %d of NGPH.\n",v,i,k); 245 | } 246 | if(v){ 247 | fprintf(fp,"%d %d\n",i,k); 248 | } 249 | } 250 | } 251 | fprintf(fp,"-1 -1\n"); 252 | } 253 | -------------------------------------------------------------------------------- /c/CountRings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "CountRings.h" 4 | 5 | int tmpmain(void) { 6 | BondType *bond; 7 | RingType *ring; 8 | int **path; 9 | int bond_n; 10 | int ring_n; 11 | int count[MAXRINGSIZE]; 12 | int ndata; 13 | int i,j; 14 | 15 | if ((bond = (BondType *) malloc(sizeof(BondType)*NMAX)) == NULL) 16 | HeapError(); 17 | if ((ring = (RingType *) malloc(sizeof(RingType)*RINGMAX)) == NULL) 18 | HeapError(); 19 | if ((path = (int **) malloc(sizeof(int *)*NMAX)) == NULL) HeapError(); 20 | for (i = 0; i < NMAX; i++) 21 | if ((path[i] = (int *) malloc(sizeof(int)*NMAX)) == NULL) 22 | HeapError(); 23 | 24 | for (ndata = 0; (bond_n = ReadBonds(bond,NMAX,stdin)) >= 0; ndata++) { 25 | if (!CheckBonds(bond,bond_n)) 26 | {fprintf(stderr,"Data error!\n"); exit(1);} 27 | 28 | /* 員環構造の探索(3点固定、重複あり) */ 29 | ring_n = CountRings(ring,RINGMAX,bond,bond_n,path,MAXRINGSIZE-1); 30 | //fprintf(stderr,"ring_n=%d \n",ring_n); 31 | 32 | /* 重複の除去 */ 33 | ring_n = SimplifyRings(ring,ring_n); 34 | //fprintf(stderr,"ring_n=%d \n",ring_n); 35 | 36 | /* 員環数ごとのカウント */ 37 | for (i = 0; i < MAXRINGSIZE; i++) 38 | count[i] = 0; 39 | for (i = 0; i < ring_n; i++) 40 | count[ring[i].n-1]++; 41 | 42 | /* 出力 */ 43 | for (i = 3; i < MAXRINGSIZE; i++) 44 | printf(" %4d",count[i-1]); 45 | printf("\n"); 46 | } 47 | 48 | /* 終了 */ 49 | for (i = 0; i < NMAX; i++) 50 | free(path[i]); 51 | free(path); 52 | free(ring); 53 | free(bond); 54 | 55 | return 0; 56 | } 57 | 58 | 59 | 60 | void 61 | BondType_CountRings( int bond_n, BondType* bond, int maxRingSize, int* count ) 62 | { 63 | RingType *ring; 64 | int **path; 65 | int ring_n; 66 | int ndata; 67 | int i,j; 68 | 69 | if ((ring = (RingType *) malloc(sizeof(RingType)*RINGMAX)) == NULL) 70 | HeapError(); 71 | if ((path = (int **) malloc(sizeof(int *)*NMAX)) == NULL) HeapError(); 72 | for (i = 0; i < NMAX; i++) 73 | if ((path[i] = (int *) malloc(sizeof(int)*NMAX)) == NULL) 74 | HeapError(); 75 | 76 | if (!CheckBonds(bond,bond_n)) { 77 | fprintf(stderr,"Data error!\n"); exit(1); 78 | } 79 | 80 | /* 員環構造の探索(3点固定、重複あり) */ 81 | ring_n = CountRings(ring,RINGMAX,bond,bond_n,path, maxRingSize ); 82 | //fprintf(stderr,"ring_n=%d \n",ring_n); 83 | 84 | /* 重複の除去 */ 85 | ring_n = SimplifyRings(ring,ring_n); 86 | //fprintf(stderr,"ring_n=%d \n",ring_n); 87 | 88 | /* 員環数ごとのカウント */ 89 | for (i = 0; i <= maxRingSize; i++) 90 | count[i] = 0; 91 | for (i = 0; i < ring_n; i++){ 92 | //printf( "%d:", ring[i].n ); 93 | count[ ring[i].n ]++; 94 | } 95 | //printf( "\n" ); 96 | 97 | /* 終了 */ 98 | for (i = 0; i < NMAX; i++) 99 | free(path[i]); 100 | free(path); 101 | free(ring); 102 | } 103 | 104 | 105 | 106 | 107 | /* 同一構造を除去 */ 108 | int SimplifyRings(RingType *ring,int ring_n) { 109 | int i,j,n; 110 | 111 | if (ring_n <= 0) return ring_n; 112 | qsort(ring,ring_n,sizeof(RingType),RingCompare); 113 | n = 0; 114 | for (i = 1; i < ring_n; i++) { 115 | if (RingCompare(ring+i,ring+n) != 0) { 116 | n++; 117 | ring[n].n = ring[i].n; 118 | for (j = 0; j < ring[i].n; j++) 119 | ring[n].list[j] = ring[i].list[j]; 120 | } 121 | } 122 | 123 | return n+1; 124 | } 125 | 126 | /* 員環情報を比較する(qsort()用関数) */ 127 | int RingCompare(const void *e1,const void *e2) { 128 | RingType *r1,*r2; 129 | int i,ret; 130 | 131 | r1 = (RingType *) e1; 132 | r2 = (RingType *) e2; 133 | for (i = 0; i < r1->n && i < r2->n && r1->list[i] == r2->list[i]; i++) 134 | ; 135 | if (i < r1->n) { 136 | if (i < r2->n) 137 | ret = (r1->list[i] >= r2->list[i])? 1: -1; 138 | else 139 | ret = -1; 140 | } else { 141 | if (i < r2->n) 142 | ret = 1; 143 | else 144 | ret = 0; 145 | } 146 | return ret; 147 | } 148 | 149 | /* n員環の数え上げ(返値は員環の総数、異常は負を返す。) */ 150 | int _crbuf_p[MAXRINGSIZE]; /* 粒子番号表 */ 151 | int _crbuf_b[MAXRINGSIZE]; /* 結合番号表 */ 152 | int _CountRings(RingType *ring,int max,BondType *bond,int **path, int maxRingSize ) { 153 | int ring_n = 0; 154 | int n,k,flag,*p; 155 | int i,j; 156 | 157 | flag = 1; 158 | for (n = 4; n <= maxRingSize && ring_n <= 0 && flag; n++) { 159 | /* n員環の探索 */ 160 | _crbuf_b[2] = 0; 161 | i = 3; 162 | flag = 0; 163 | while (i >= 3) { 164 | if (i >= n) { 165 | flag = 1; 166 | if (path[_crbuf_p[0]][_crbuf_p[n-1]] == 1) { 167 | /* n員環を発見、登録 */ 168 | if (ring_n < max) { 169 | ring[ring_n].n = n; 170 | k = 0; 171 | for (j = 1; j < n; j++) 172 | if (_crbuf_p[j] < _crbuf_p[k]) 173 | k = j; 174 | if (_crbuf_p[(k-1+n)%n] > _crbuf_p[(k+1)%n]) { 175 | for (j = 0; j < n; j++) 176 | ring[ring_n].list[j] = _crbuf_p[(k+j)%n]; 177 | } else { 178 | for (j = 0; j < n; j++) 179 | ring[ring_n].list[j] = _crbuf_p[(k-j+n)%n]; 180 | } 181 | ring_n++; 182 | } else 183 | return -1; /* 登録不能、異常終了 */ 184 | } 185 | } else { 186 | if (_crbuf_b[i-1] < bond[_crbuf_p[i-1]].n) { 187 | _crbuf_p[i] = bond[_crbuf_p[i-1]].to[_crbuf_b[i-1]]; 188 | /* 粒子間距離の判定 */ 189 | p = path[_crbuf_p[i]]; 190 | for (j = 0; j < i-1 191 | && p[_crbuf_p[j]] >= _crmin(i-j,n-i+j); j++) 192 | ; 193 | if (j >= i-1) { 194 | /* 採択。1つ下がる */ 195 | _crbuf_b[i++] = 0; 196 | } else { 197 | /* 棄却。次を試す */ 198 | _crbuf_b[i-1]++; 199 | } 200 | continue; 201 | } 202 | } 203 | /* 1つ上がる */ 204 | i--; 205 | _crbuf_b[i-1]++; /* 最後に_crbuf_b[1]++をしてしまうが問題なし */ 206 | } 207 | } 208 | 209 | return ring_n; 210 | } 211 | 212 | /* 全員環数の数え上げ(返値は員環の総数、異常は負を返す。) */ 213 | int CountRings(RingType *ring,int max,BondType *bond,int bond_n,int **path, int maxRingSize ) { 214 | int p0,p1,p2,b0,b1,b2; 215 | int ring_n = 0; 216 | int ret; 217 | int i,j; 218 | 219 | /* 各粒子間の最小距離を求める */ 220 | MinPath(path,bond,bond_n); 221 | 222 | /* メインループ */ 223 | for (p0 = 0; p0 < bond_n; p0++) { 224 | for (b0 = 0; b0 < bond[p0].n; b0++) { 225 | p1 = bond[p0].to[b0]; 226 | for (b1 = 0; b1 < bond[p1].n; b1++) { 227 | p2 = bond[p1].to[b1]; 228 | if (p0 >= p2) continue; 229 | if (path[p0][p2] == 1) { 230 | /* 3員環発見、登録 */ 231 | if (p0 < p1 && p1 < p2) { 232 | if (ring_n < max) { 233 | ring[ring_n].n = 3; 234 | ring[ring_n].list[0] = p0; 235 | ring[ring_n].list[1] = p1; 236 | ring[ring_n].list[2] = p2; 237 | ring_n++; 238 | } else 239 | return -1; /* 登録不能、異常終了 */ 240 | } 241 | } else { 242 | /* n員環探索(n>3) */ 243 | _crbuf_p[0] = p0; 244 | _crbuf_p[1] = p1; 245 | _crbuf_p[2] = p2; 246 | _crbuf_b[0] = b0; 247 | _crbuf_b[1] = b1; 248 | _crbuf_b[2] = 0; 249 | ret = _CountRings(ring+ring_n,max-ring_n,bond,path, maxRingSize ); 250 | if (ret >= 0) 251 | ring_n += ret; 252 | else 253 | return -1; /* 登録不能、異常終了 */ 254 | } 255 | } 256 | } 257 | } 258 | 259 | return ring_n; 260 | } 261 | 262 | /* path[i][j]にi〜jへの最短路の距離を求める */ 263 | void MinPath(int **path,BondType *bond,int bond_n) { 264 | int work[NMAX]; 265 | int head,tail; 266 | int *p; 267 | int i,j,k,to,length; 268 | 269 | /* 初期化 */ 270 | for (i = 0; i < bond_n; i++) 271 | for (j = 0; j < bond_n; j++) 272 | path[i][j] = bond_n; /* 未計算の意味 */ 273 | 274 | for (i = 0; i < bond_n; i++) { 275 | p = path[i]; 276 | head = 0; 277 | tail = 1; 278 | p[i] = 0; 279 | work[0] = i; 280 | while (tail > head && tail < bond_n) { 281 | k = work[head++]; 282 | length = p[k]+1; 283 | for (j = 0; j < bond[k].n; j++) { 284 | to = bond[k].to[j]; 285 | if (p[to] >= bond_n) { 286 | /* 新規登録 */ 287 | work[tail++] = to; 288 | } 289 | if (p[to] > length) 290 | p[to] = length; 291 | } 292 | } 293 | } 294 | } 295 | 296 | /* 挿入ソート */ 297 | void _insertsort(int n,int *a) { 298 | int i,j; 299 | int x; 300 | 301 | for (i = 1; i < n; i++) { 302 | x = a[i]; 303 | for (j = i-1; j >= 0 && a[j] > x; j--) 304 | a[j+1] = a[j]; 305 | a[j+1] = x; 306 | } 307 | } 308 | 309 | /* 有向グラフの読み込み(返値は粒子数、失敗は負。) */ 310 | int ReadBonds(BondType *bond,int max,FILE *fp) { 311 | char str[NMAX+1]; 312 | int n; 313 | int i,j; 314 | 315 | /* 初期化 */ 316 | for (i = 0; i < max; i++) 317 | bond[i].n = 0; 318 | 319 | /* 読み込み */ 320 | n = max; 321 | for (i = 0; (i < n) && (fgets(str,NMAX+1,fp) != NULL); i++) { 322 | for (j = 0; (j < n) && ((str[j] == '0') || (str[j] == '1')); j++) { 323 | if (j != i && str[j] == '1') { 324 | if ((bond[i].n >= MAXBOND) || (bond[j].n >= MAXBOND)) 325 | {fprintf(stderr,"%d: Too many bonds.\n",i); return -1;} 326 | bond[i].to[bond[i].n++] = j; 327 | bond[j].to[bond[j].n++] = i; 328 | } 329 | } 330 | if (i == 0) n = j; 331 | } 332 | if ((i < n) || ((fgets(str,NMAX+1,fp) != NULL) && (*str != '\0') && (*str != '\n'))) 333 | return -1; 334 | 335 | /* ソート */ 336 | for (i = 0; i < n; i++) 337 | _insertsort(bond[i].n, bond[i].to); 338 | 339 | return n; 340 | } 341 | 342 | /* データの整合性判定 */ 343 | int CheckBonds(BondType *bond,int bond_n) { 344 | int i,j,k; 345 | int to; 346 | 347 | for (i = 0; i < bond_n; i++) { 348 | for (j = 0; j < bond[i].n; j++) { 349 | to = bond[i].to[j]; 350 | if ((to < 0) || (to >= bond_n) || ((j > 0) && (to <= bond[i].to[j-1]))) { 351 | fprintf(stderr,"%2d,%2d: Illegal bond %d found(1).\n",i,j,to); 352 | return 0; 353 | } 354 | for (k = 0; k < bond[to].n && bond[to].to[k] != i; k++) 355 | ; 356 | if (k >= bond[to].n) { 357 | fprintf(stderr,"%2d: Illegal bond %d found(2).\n",i,to); 358 | return 0; 359 | } 360 | } 361 | } 362 | return 1; 363 | } 364 | 365 | void HeapError(void) { 366 | fprintf(stderr,"Allocation failed.\n"); exit(1); 367 | } 368 | 369 | 370 | /*以上は馬場君(?)が作った算程群。基本的なアルゴリズムはこれでいいのだ 371 | が、巨大なシステムの場合、完全なpath[i][j]を作成するのは非常に浪費な 372 | ので、なんとか簡約したい。例えば、6員環までを知りたい場合、距離は3ま 373 | でで十分。任意に与えられた2点間の距離が即座に計算できれば、path[][] 374 | は必要ない。hash表で解決できるかも。 375 | 376 | 周期境界を横断するような環ができるケースがある。xy面、yz面、zx面を辺が 377 | 交差した回数をカウントしておけば、そのような環を(座標の情報なしで)除く 378 | ことができるはず。*/ 379 | 380 | 381 | /*距離4以下の点をリストアップする。距離1の点対はあらかじめ 382 | RegisterDistanceで登録してあるものとする。無向グラフなら、i->jとj->i 383 | の両方が書かれていなければならない。*/ 384 | 385 | 386 | /*どうせこれはそのまま使うことはできないぞ。*/ 387 | --------------------------------------------------------------------------------