├── data
└── rapa_nui
│ ├── ahu.cpg
│ ├── ahu.dbf
│ ├── ahu.shp
│ ├── ahu.shx
│ ├── ahu.prj
│ └── ahu.qpj
├── networkdrift
├── __init__.py
├── utils
│ ├── __init__.py
│ └── utils.py
└── demography
│ ├── __init__.py
│ └── network.py
├── required-modules.txt
├── .idea
└── other.xml
├── setup.py
├── simuPOP_examples
├── islandMigration.py
├── islandMigration-FromCookbook.py
├── geneticDrift.py
├── simuOptParam2argparse.py
├── simuOptToParamsConversion.py
└── simuMigration.py
├── testdata
├── test_graph.gml
├── island_test.gml
└── test-network.gml
├── README.md
└── models
├── shapefile_to_gml.py
├── parameter-sweep-for-localization.py
├── network-subpop-eval.py
└── network-k-eval.py
/data/rapa_nui/ahu.cpg:
--------------------------------------------------------------------------------
1 | UTF-8
--------------------------------------------------------------------------------
/networkdrift/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/networkdrift/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/networkdrift/demography/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/rapa_nui/ahu.dbf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nevrome/network-drift/master/data/rapa_nui/ahu.dbf
--------------------------------------------------------------------------------
/data/rapa_nui/ahu.shp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nevrome/network-drift/master/data/rapa_nui/ahu.shp
--------------------------------------------------------------------------------
/data/rapa_nui/ahu.shx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nevrome/network-drift/master/data/rapa_nui/ahu.shx
--------------------------------------------------------------------------------
/required-modules.txt:
--------------------------------------------------------------------------------
1 | datetime
2 | matplotlib
3 | operator
4 | simuPOP
5 | argparse
6 | itertools
7 | logging
8 | math
9 | numpy
10 | random
11 | sys
12 | uuid
13 | scipy
14 | time
15 | seaborn
16 | networkx
17 |
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from distutils.core import setup
4 |
5 | setup(name='network_drift',
6 | version='1.0',
7 | description='Python Distribution Utilities',
8 | packages=['networkdrift.demography', 'networkdrift.utils'],
9 | )
10 |
--------------------------------------------------------------------------------
/data/rapa_nui/ahu.prj:
--------------------------------------------------------------------------------
1 | PROJCS["WGS_1984_UTM_Zone_12S",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-111],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["Meter",1]]
--------------------------------------------------------------------------------
/data/rapa_nui/ahu.qpj:
--------------------------------------------------------------------------------
1 | PROJCS["WGS 84 / UTM zone 12S",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-111],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32712"]]
2 |
--------------------------------------------------------------------------------
/simuPOP_examples/islandMigration.py:
--------------------------------------------------------------------------------
1 |
2 | import simuPOP as sim
3 | from simuPOP.utils import migrIslandRates
4 |
5 | p = [0.2, 0.3, 0.5]
6 |
7 | pop = sim.Population(size=[10000]*3, loci=1, infoFields='migrate_to')
8 | simu = sim.Simulator(pop, rep=2)
9 |
10 | simu.evolve(
11 | initOps=[sim.InitSex()] +
12 | [sim.InitGenotype(prop=[p[i], 1-p[i]], subPops=i) for i in range(3)],
13 |
14 | preOps=sim.Migrator(rate=migrIslandRates(0.01, 3), reps=0),
15 | matingScheme=sim.RandomMating(),
16 | postOps=[
17 | sim.Stat(alleleFreq=0, structure=0, vars='alleleFreq_sp', step=50),
18 | sim.PyEval(r"'Fst=%.3f (%s)' % (F_st, ', '.join(['%.2f’ % "
19 | "subPop[x]['alleleFreq'][0][0] for x in range(3)]))",
20 | step=50),
21 | sim.PyOutput('\n', reps=-1, step=50),
22 | ],
23 | gen=201
24 | )
25 |
26 |
--------------------------------------------------------------------------------
/testdata/test_graph.gml:
--------------------------------------------------------------------------------
1 | graph [
2 | name "island_network"
3 | node [
4 | id 0
5 | label "island_0"
6 | x_coord "30"
7 | y_coord "10"
8 | ]
9 | node [
10 | id 1
11 | label "island_1"
12 | x_coord "20"
13 | y_coord "30"
14 | ]
15 | node [
16 | id 2
17 | label "island_2"
18 | x_coord "10"
19 | y_coord "10"
20 | ]
21 | node [
22 | id 3
23 | label "island_3"
24 | x_coord "20"
25 | y_coord "0"
26 | ]
27 | node [
28 | id 4
29 | label "island_4"
30 | x_coord "10"
31 | y_coord "20"
32 | ]
33 | edge [
34 | id 0
35 | source 0
36 | target 1
37 | value 1.0
38 | ]
39 | edge [
40 | id 1
41 | source 1
42 | target 2
43 | value 0.5
44 | ]
45 | edge [
46 | id 2
47 | source 2
48 | target 3
49 | value 0.5
50 | ]
51 | edge [
52 | id 3
53 | source 4
54 | target 0
55 | value 1.0
56 | ]
57 | edge [
58 | id 4
59 | source 4
60 | target 3
61 | value 0.5
62 | ]
63 | edge
64 | [ id 5
65 | source 3
66 | target 1
67 | value 1.0
68 | ]
69 | edge
70 | [ id 6
71 | source 2
72 | target 4
73 | value 0.5
74 | ]
75 | edge [
76 | id 7
77 | source 1
78 | target 4
79 | value 1.0
80 | ]
81 | ]
82 |
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Network-drift
2 |
3 | Cultural transmission models in python using simuPOP, SciPy, and NumPy.
4 |
5 | network-drift is a python-based software layer on top of simuPOP by Bo Peng, currently version 1.9. See http://simupop.sourceforge.net/ for source code, license, and documentation.
6 |
7 | Directory Structure
8 |
9 | demography : python modules for the population structure. Currently, this consists just of network configuration. Networks can be configured as GML files or by specifiying numbers of nodes and connectivity (producing small-world graphs).
10 |
11 | models : python script to run the simulation models.
12 |
13 | simuPOP_examples : python scripts that demonstrate features of simuPOP from documentation and cookbooks.
14 |
15 | testdata : sample GML files
16 |
17 | Module Dependencies
18 |
19 | find . -name "*.py" | xargs grep -h 'import ' | grep -v simu | sort | cut -d' ' -f2 | uniq > required-modules.txt
20 |
21 | Author
22 |
23 | Carl P. Lipo and Mark E. Madsen Copyright 2018-2019. All rights reserved. This software is made available under the Apache Software License (see file LICENSE), which allows you to use the software for commercial or non-commercial purposes, but you must attribute authorship, and derivatives must allow the user to find the original code and license.
24 |
--------------------------------------------------------------------------------
/simuPOP_examples/islandMigration-FromCookbook.py:
--------------------------------------------------------------------------------
1 | # this example is from Antao's 2015 - Bioinformation with Python Cookbook
2 | # Chapter 5 - Simulating population structure using island and stepping-stone models
3 | # page 138
4 |
5 | from collections import defaultdict, OrderedDict
6 | from copy import deepcopy
7 | import simuPOP as sp
8 | from simuPOP import demography
9 | num_loci = 10
10 | pop_size = 50
11 | num_gens = 101
12 | num_pops = 10
13 | migs = [0, 0.005, 0.01, 0.02, 0.05, 0.1]
14 | init_ops = OrderedDict()
15 | pre_ops = OrderedDict()
16 | post_ops = OrderedDict()
17 | pops = sp.Population([pop_size] * num_pops, loci=[1] *
18 | num_loci, infoFields=['migrate_to'])
19 |
20 |
21 | def init_accumulators(pop, param):
22 | accumulators = param
23 | for accumulator in accumulators:
24 | if accumulator.endswith('_sp'):
25 | pop.vars()[accumulator] = defaultdict(list)
26 | else:
27 | pop.vars()[accumulator] = []
28 | return True
29 |
30 |
31 | def update_accumulator(pop, param):
32 | accumulator, var = param
33 | if var.endswith('_sp'):
34 | for sp in range(pop.numSubPop()):
35 | pop.vars()[accumulator][sp].append(
36 | deepcopy(pop.vars(sp)[var[:-3]]))
37 | else:
38 | pop.vars()[accumulator].append(deepcopy(pop.vars()[var]))
39 | return True
40 |
41 | init_ops['accumulators'] = sp.PyOperator(init_accumulators,
42 | param=['fst'])
43 | init_ops['Sex'] = sp.InitSex()
44 | init_ops['Freq'] = sp.InitGenotype(freq=[0.5, 0.5])
45 | for i, mig in enumerate(migs):
46 | post_ops['mig-%d' % i] = sp.Migrator(demography.migrIslandRates(mig, num_pops),
47 | reps=[i])
48 | post_ops['Stat-fst'] = sp.Stat(structure=sp.ALL_AVAIL)
49 | post_ops['fst_accumulation'] = sp.PyOperator(update_accumulator, param=('fst', 'F_st'))
50 |
51 | mating_scheme = sp.RandomMating()
52 |
53 | sim = sp.Simulator(pops, rep=len(migs))
54 | sim.evolve(initOps=list(init_ops.values()),
55 | preOps=list(pre_ops.values()),
56 | postOps=list(post_ops.values()),
57 | matingScheme=mating_scheme,
58 | gen=num_gens)
59 |
60 | import seaborn as sns
61 | sns.set_style('white')
62 | import matplotlib.pyplot as plt
63 | fig = plt.figure(figsize=(16, 9))
64 | ax = fig.add_subplot(111)
65 | for i, pop in enumerate(sim.populations()):
66 | ax.plot(pop.dvars().fst, label='mig rate %.4f' % migs[i])
67 | ax.legend(loc=2)
68 | ax.set_ylabel('FST')
69 | ax.set_xlabel('Generation')
70 |
--------------------------------------------------------------------------------
/simuPOP_examples/geneticDrift.py:
--------------------------------------------------------------------------------
1 |
2 | """
3 | This program demonstrates changes of allele frequency on single locus due to genetic drift.
4 | """
5 |
6 | import simuOpt, os, sys, time
7 | from simuPOP import *
8 | import argparse
9 | import rapanuisim.utils as utils
10 |
11 | try:
12 | from simuPOP.plotter import VarPlotter
13 | except:
14 | print("simuRPy import failed. Please check your rpy installation.")
15 | print("Allele Frequencies will not be plotted")
16 | useRPy = False
17 | else:
18 | useRPy = True
19 |
20 | def simuGeneticDrift(popSize=100, p=0.2, generations=100, replications=5):
21 | '''Simulate the Genetic Drift as a result of random mating.'''
22 | # diploid population, one chromosome with 1 locus
23 | # random mating with sex
24 | pop = Population(size=popSize, loci=args.numloci)
25 |
26 | initial_distribution = utils.constructUniformAllelicDistribution(args.numloci)
27 |
28 | simu = Simulator(pop, rep=replications)
29 |
30 | if useRPy:
31 | plotter = VarPlotter('alleleFreq[0][0]', ylim=[0, 1], ylab='allele frequency',
32 | update=generations - 1, saveAs='geneticDrift.png')
33 | else:
34 | plotter = NoneOp()
35 |
36 | # if number of generation is smaller than 200, step is 10 generations,
37 | # if it's between 200 and 500, set step to be 20 generations,
38 | # otherwise, step = 50 generations.
39 | if generations <= 200:
40 | s = 10
41 | elif 200 < generations <= 500:
42 | s = 20
43 | else:
44 | s = 50
45 |
46 | simu.evolve(
47 | # everyone initially will have the same allele frequency
48 | initOps=[
49 | InitSex(),
50 | InitGenotype(freq=[p, 1 - p])
51 | ],
52 | matingScheme=RandomMating(),
53 | postOps=[
54 | Stat(alleleFreq=[0]),
55 | PyEval(r'"Generation %d:\t" % gen', reps=0, step=s),
56 | PyEval(r"'%.3f\t' % alleleFreq[0][0]", step=s),
57 | PyOutput('\n', reps=-1, step=s),
58 | plotter,
59 | ],
60 | gen=generations
61 | )
62 |
63 |
64 | if __name__ == '__main__':
65 | # get all parameters
66 | args = argparse.ArgumentParser()
67 | args.add_argument("--popSize",
68 | default=100,
69 | type=int,
70 | help="Population Size")
71 | args.add_argument("--p",
72 | default=0.05,
73 | type=float,
74 | help="Initial Allele Frequency")
75 | args.add_argument("--generations",
76 | default=100,
77 | type=int,
78 | help="Number of Generations")
79 | args.add_argument("--replications",
80 | default=8,
81 | type=int,
82 | help="Number of Replicates")
83 | args.add_argument("--numloci",
84 | default=30,
85 | type=int,
86 | help="Number of loci to model (number of features)")
87 | args = args.parse_args()
88 |
89 | simuGeneticDrift(args.popSize, args.p, args.generations, args.replications)
90 |
91 | # wait ten seconds before exit
92 | if useRPy:
93 | print("Figure will be closed after 5 seconds.")
94 | time.sleep(5)
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/testdata/island_test.gml:
--------------------------------------------------------------------------------
1 | graph [
2 | name "island_network"
3 | node [
4 | id 0
5 | label "0"
6 | SUID "0"
7 | sharedname "island_0"
8 | name "island_4"
9 | selected "false"
10 | x_coord "30"
11 | y_coord "10"
12 | ]
13 | node [
14 | id 1
15 | label "1"
16 | SUID "2"
17 | sharedname "island_1"
18 | name "island_1"
19 | selected "false"
20 | x_coord "20"
21 | y_coord "30"
22 | ]
23 | node [
24 | id 2
25 | label "2"
26 | sharedname "island_2"
27 | name "island_2"
28 | selected "false"
29 | x_coord "10"
30 | y_coord "10"
31 | ]
32 | node [
33 | id 3
34 | label "3"
35 | sharedname "island_3"
36 | name "island_3"
37 | selected "false"
38 | x_coord "20"
39 | y_coord "0"
40 | ]
41 | node [
42 | id 4
43 | label "4"
44 | SUID "4"
45 | sharedname "island_4"
46 | name "island_4"
47 | selected "false"
48 | x_coord "10"
49 | y_coord "20"
50 | ]
51 | edge [
52 | id 0
53 | source 0
54 | target 1
55 | value 1.0
56 | SUID "104"
57 | sharedname "Node 0 (interaction type) Node 1"
58 | sharedinteraction "interaction type"
59 | name "Node 0 (interaction type) Node 1"
60 | selected "false"
61 | interaction "interaction type"
62 | ]
63 | edge [
64 | id 1
65 | source 1
66 | target 2
67 | value 0.5
68 | SUID "103"
69 | sharedname "Node 1 (interaction type) Node 2"
70 | sharedinteraction "interaction type"
71 | name "Node 1 (interaction type) Node 2"
72 | selected "false"
73 | interaction "interaction type"
74 | ]
75 | edge [
76 | id 2
77 | source 2
78 | target 3
79 | value 0.5
80 | SUID "102"
81 | sharedname "Node 2 (interaction type) Node 3"
82 | sharedinteraction "interaction type"
83 | name "Node 2 (interaction type) Node 3"
84 | selected "false"
85 | interaction "interaction type"
86 | ]
87 | edge [
88 | id 3
89 | source 4
90 | target 0
91 | value 1.0
92 | SUID "101"
93 | sharedname "Node 4 (interaction type) Node 0"
94 | sharedinteraction "interaction type"
95 | name "Node 4 (interaction type) Node 0"
96 | selected "false"
97 | interaction "interaction type"
98 | ]
99 | edge [
100 | id 4
101 | source 4
102 | target 3
103 | value 0.5
104 | SUID "100"
105 | sharedname "Node 4 (interaction type) Node 3"
106 | sharedinteraction "interaction type"
107 | name "Node 4 (interaction type) Node 3"
108 | selected "false"
109 | interaction "interaction type"
110 | ]
111 | edge
112 | [ id 5
113 | source 3
114 | target 1
115 | value 1.0
116 | SUID "99"
117 | sharedname "Node 3 (interaction type) Node 1"
118 | sharedinteraction "interaction type"
119 | name "Node 3 (interaction type) Node 1"
120 | selected "false"
121 | interaction "interaction type"
122 | ]
123 | edge
124 | [ id 6
125 | source 2
126 | target 4
127 | value 0.5
128 | SUID "98"
129 | sharedname "Node 2 (interaction type) Node 4"
130 | sharedinteraction "interaction type"
131 | name "Node 2 (interaction type) Node 4"
132 | selected "false"
133 | interaction "interaction type"
134 | ]
135 | edge [
136 | id 7
137 | source 1
138 | target 4
139 | value 1.0
140 | SUID "97"
141 | sharedname "Node 1 (interaction type) Node 4"
142 | sharedinteraction "interaction type"
143 | name "Node 1 (interaction type) Node 4"
144 | selected "true"
145 | interaction "interaction type"
146 | ]
147 | ]
148 |
--------------------------------------------------------------------------------
/simuPOP_examples/simuOptParam2argparse.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #
4 | #
5 | # To perform conversion:
6 | #
7 | # python simuOptParam2argparse.py filename start_lineno ending_lineno
8 | #
9 |
10 | import argparse
11 |
12 |
13 | def convert_options(filename, startline, endline, name):
14 | with open(filename) as script:
15 | content = ''.join(script.readlines()[startline: endline])
16 | prefix = '''
17 | class FakeSimuOptClass:
18 | def __init__(self):
19 | for name in [
20 | 'valueNot',
21 | 'valueOr',
22 | 'valueAnd',
23 | 'valueOneOf',
24 | 'valueTrueFalse',
25 | 'valueBetween',
26 | 'valueGT',
27 | 'valueGE',
28 | 'valueLT',
29 | 'valueLE',
30 | 'valueEqual',
31 | 'valueNotEqual',
32 | 'valueIsInteger',
33 | 'valueIsNum',
34 | 'valueIsList',
35 | 'valueValidDir',
36 | 'valueValidFile',
37 | 'valueListOf',
38 | 'valueSumTo']:
39 | setattr(self, name, lambda *args, **kwargs: None)
40 | simuOpt = FakeSimuOptClass()
41 | '''
42 | # replace list
43 | replaces = [
44 | ]
45 |
46 | for old, new in replaces:
47 | content = content.replace(old, new)
48 | value = {}
49 | exec(prefix + content, value)
50 |
51 | print('import argparse')
52 | print('args = argparse.ArgumentParser()')
53 | for opt in value[name]:
54 | if 'name' not in opt:
55 | continue
56 | name = opt['name']
57 | default = "\n default={!r},".format(opt['default']) if 'default' in opt else None
58 | type = opt['type'] if 'type' in opt else None
59 | help = opt['label'] if 'label' in opt else ''
60 | help = (help + '\n ' + opt['description']) if 'description' in opt else help
61 | if 'type' in opt:
62 | if opt['type'] == 'filename':
63 | topt = ''
64 | elif not isinstance(opt['type'], (list, tuple)):
65 | if hasattr(opt['type'], '__name__'):
66 | topt = '\n type={},'.format(opt['type'].__name__)
67 | elif opt['type'] == 'integers':
68 | topt = '\n nargs="*",\n type=int,'
69 | elif opt['type'] == 'numbers':
70 | topt = '\n nargs="*",\n type=float,'
71 | elif opt['type'] == 'integer':
72 | topt = '\n type=int,'
73 | elif opt['type'] == 'number':
74 | topt = '\n type=float,'
75 | elif opt['type'] == 'string':
76 | pass
77 | elif opt['type'] == 'strings':
78 | topt = '\n nargs="*",'
79 | else:
80 | topt = '\n type={},'.format(opt['type'])
81 | else:
82 | topt = '\n nargs="*",'
83 | print('args.add_argument("--{}",{}{}\n help="""{}""")'.format(name, default, topt, help))
84 | print('args = args.parse_args()')
85 |
86 |
87 | if __name__ == '__main__':
88 | parser = argparse.ArgumentParser('simuOptParam2argparse.py',
89 | description='''This script converts a now deprecated simuOpt.Param
90 | list to argparse definitions. It is intended to be a
91 | semi-automatic process that assist your conversion from
92 | simuOpt.Param to argparser.
93 | ''')
94 | parser.add_argument('filename', help='''file that contains the
95 | simuOpt option list.''')
96 | parser.add_argument('startline', type=int,
97 | help='''starting line of the option list''')
98 | parser.add_argument('endline', type=int,
99 | help='''last line of the option list''')
100 | parser.add_argument('name',
101 | help='''Name of the option list.''')
102 | args = parser.parse_args()
103 | #
104 | convert_options(args.filename, args.startline, args.endline, args.name)
--------------------------------------------------------------------------------
/simuPOP_examples/simuOptToParamsConversion.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #
4 | #
5 | # To perform conversion:
6 | #
7 | # python simuOptParam2argparse.py filename start_lineno ending_lineno
8 | #
9 |
10 | import argparse
11 |
12 |
13 | def convert_options(filename, startline, endline, name):
14 | with open(filename) as script:
15 | content = ''.join(script.readlines()[startline: endline])
16 | prefix = '''
17 | class FakeSimuOptClass:
18 | def __init__(self):
19 | for name in [
20 | 'valueNot',
21 | 'valueOr',
22 | 'valueAnd',
23 | 'valueOneOf',
24 | 'valueTrueFalse',
25 | 'valueBetween',
26 | 'valueGT',
27 | 'valueGE',
28 | 'valueLT',
29 | 'valueLE',
30 | 'valueEqual',
31 | 'valueNotEqual',
32 | 'valueIsInteger',
33 | 'valueIsNum',
34 | 'valueIsList',
35 | 'valueValidDir',
36 | 'valueValidFile',
37 | 'valueListOf',
38 | 'valueSumTo']:
39 | setattr(self, name, lambda *args, **kwargs: None)
40 | simuOpt = FakeSimuOptClass()
41 | '''
42 | # replace list
43 | replaces = [
44 | ]
45 |
46 | for old, new in replaces:
47 | content = content.replace(old, new)
48 | value = {}
49 | exec(prefix + content, value)
50 |
51 | print('import argparse')
52 | print('args = argparse.ArgumentParser()')
53 | for opt in value[name]:
54 | if 'name' not in opt:
55 | continue
56 | name = opt['name']
57 | default = "\n default={!r},".format(opt['default']) if 'default' in opt else None
58 | type = opt['type'] if 'type' in opt else None
59 | help = opt['label'] if 'label' in opt else ''
60 | help = (help + '\n ' + opt['description']) if 'description' in opt else help
61 | if 'type' in opt:
62 | if opt['type'] == 'filename':
63 | topt = ''
64 | elif not isinstance(opt['type'], (list, tuple)):
65 | if hasattr(opt['type'], '__name__'):
66 | topt = '\n type={},'.format(opt['type'].__name__)
67 | elif opt['type'] == 'integers':
68 | topt = '\n nargs="*",\n type=int,'
69 | elif opt['type'] == 'numbers':
70 | topt = '\n nargs="*",\n type=float,'
71 | elif opt['type'] == 'integer':
72 | topt = '\n type=int,'
73 | elif opt['type'] == 'number':
74 | topt = '\n type=float,'
75 | elif opt['type'] == 'string':
76 | pass
77 | elif opt['type'] == 'strings':
78 | topt = '\n nargs="*",'
79 | else:
80 | topt = '\n type={},'.format(opt['type'])
81 | else:
82 | topt = '\n nargs="*",'
83 | print('args.add_argument("--{}",{}{}\n help="""{}""")'.format(name, default, topt, help))
84 | print('args = args.parse_args()')
85 |
86 |
87 | if __name__ == '__main__':
88 | parser = argparse.ArgumentParser('simuOptParam2argparse.py',
89 | description='''This script converts a now deprecated simuOpt.Param
90 | list to argparse definitions. It is intended to be a
91 | semi-automatic process that assist your conversion from
92 | simuOpt.Param to argparser.
93 | ''')
94 | parser.add_argument('filename', help='''file that contains the
95 | simuOpt option list.''')
96 | parser.add_argument('startline', type=int,
97 | help='''starting line of the option list''')
98 | parser.add_argument('endline', type=int,
99 | help='''last line of the option list''')
100 | parser.add_argument('name',
101 | help='''Name of the option list.''')
102 | args = parser.parse_args()
103 | #
104 | convert_options(args.filename, args.startline, args.endline, args.name)
--------------------------------------------------------------------------------
/simuPOP_examples/simuMigration.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Demonstrate the changes of allele frequencies among subpopulations due to migration
4 | #
5 |
6 | """
7 | This program demonstrates the effect of migration by rate which results in
8 | changes of allele frequencies among subpopulations.
9 | """
10 |
11 | import simuOpt as sim
12 | import os, sys, types, time
13 | from simuPOP import *
14 | from simuOpt import *
15 | import argparse
16 |
17 | try:
18 | from simuPOP.plotter import VarPlotter
19 | except:
20 | print("simuRPy import failed. Please check your rpy installation.")
21 | print("allele frquencies in subpopulations will not be plotted")
22 | useRPy = False
23 | else:
24 | useRPy = True
25 |
26 | def simuMigration(subPopSize, numOfSubPops, m, generations, numLoci):
27 | '''Simulate the change of allele frequencies among subpopulations as a result of migration.'''
28 | # diploid population, one chromosome with 1 locus
29 | # random mating with sex
30 | pop = Population(size=[subPopSize] * numOfSubPops, loci=numLoci, infoFields=['migrate_to'], ancGen=-1, ploidy=1 )
31 | # set initial allele frequencies to each subpopulation
32 | # Rule of initialization is to use a list of numbers, which have counts equal to number of
33 | # subpopulations and been set evenly distributed between 0 and 1.
34 | # Therefore, the average allele frequency named theoretical value among all subpopulations will be 0.5
35 | for i in range(numOfSubPops):
36 | initGenotype(pop, freq=[i * 1. / (numOfSubPops - 1), 1 - i * 1. / (numOfSubPops - 1)], subPops=[i])
37 |
38 | # check if plot
39 | if useRPy:
40 | plotter = VarPlotter('[0.5] + [subPop[i]["alleleFreq"][0][0] for i in range(%d)]' % numOfSubPops,
41 | ylim=[0, 1], update=generations - 1,
42 | legend=['Theoretical'] + ['Allele freq at subpop %d' % x for x in range(numOfSubPops)],
43 | xlab="generation", ylab="alleleFrequency", saveAs='migration.png')
44 | else:
45 | plotter = NoneOp()
46 |
47 | a = []
48 | # set migration rate matrix a[]. Within each row i of matrix a[], all elements have the same value which is
49 | # m divided by number of subpopulations minus one, except the diagonal element, whose value is set to be 0.
50 | # This setting ensures that every individual in subpopulation i has probability m to become the Migrator,
51 | # and such a migrator.has equal possibility of entering any other subpopulation.
52 | for i in range(numOfSubPops):
53 | b = [];
54 | for j in range(numOfSubPops):
55 | if j == i:
56 | b.append(0)
57 | else:
58 | b.append(m / (numOfSubPops - 1))
59 | a.append(b)
60 | # if number of generations is smaller than 200, step will be set to 10 generations, otherwise it will be 20.
61 | if generations <= 200:
62 | s = 10
63 | else:
64 | s = 20
65 | pop.evolve(
66 | initOps=InitSex(),
67 | preOps=[
68 | Stat(alleleFreq=[0], vars=['alleleFreq_sp']),
69 | PyEval(r'"Frequency at generation %d: %s\n" % (gen, ", ".join(["%.2f" % x for x in freq]))',
70 | stmts='freq = [subPop[x]["alleleFreq"][0][0] for x in range(%i)]' % numOfSubPops, step=s),
71 |
72 | ],
73 | matingScheme=RandomMating(ops=CloneGenoTransmitter()),
74 | postOps=[
75 | KAlleleMutator(k=1, rates=0.01),
76 | Migrator(rate=a),
77 | plotter,
78 | ],
79 | gen=generations,
80 | )
81 |
82 |
83 | if __name__ == '__main__':
84 |
85 | args = argparse.ArgumentParser()
86 | args.add_argument("--subPopSize",
87 | default=5000,
88 | type=int,
89 | help="SubPopulation Size")
90 | args.add_argument("--numOfSubPops",
91 | default=20,
92 | type=int,
93 | help="Number of Sub Populations")
94 | args.add_argument("--m",
95 | default=0.05,
96 | type=float,
97 | help="Migration Rate")
98 | args.add_argument("--generations",
99 | default=1000,
100 | type=int,
101 | help="Generations")
102 | args.add_argument("--numLoci",
103 | default=5,
104 | type=int,
105 | help="Number of Loci")
106 | args = args.parse_args()
107 |
108 | simuMigration(args.subPopSize, args.numOfSubPops, args.m, args.generations, args.numLoci)
109 | # wait five seconds before exit
110 | if useRPy:
111 | print("Figure will be closed after five seconds.")
112 | time.sleep(5)
--------------------------------------------------------------------------------
/models/shapefile_to_gml.py:
--------------------------------------------------------------------------------
1 | import gdal
2 | import networkx as nx
3 | import matplotlib.pyplot as plt
4 | from collections import defaultdict
5 | from itertools import permutations
6 | import operator
7 | import math
8 | import statistics
9 | import argparse
10 |
11 | global config
12 |
13 | '''
14 | Example use of script.
15 | python3 ./shapefile_to_gml.py
16 | --shapefile ../data/rapa_nui/ahu.shp
17 | --migrationfraction 0.0001
18 | --connectedness 5
19 | --nodecolor blue
20 | --output ahu
21 |
22 | '''
23 |
24 | def print_graph(network,pos, nodecolor):
25 | """
26 | Show us the graph of the network.
27 | :return: nothing - should be a matplotlib plot
28 | """
29 | plt.subplot(111)
30 | nx.draw(network, pos,
31 | node_color=nodecolor,
32 | node_size=30,
33 | width=0.1,
34 | with_labels=False, font_weight='bold')
35 | plt.show()
36 | return True
37 |
38 | def save_graph_plot(network,pos,outputname,connectedness,nodecolor):
39 | """
40 | Save the graph of the network.
41 | :return: nothing - should be saved file
42 | """
43 | name = "%s-%s.svg" % (outputname, connectedness)
44 | nx.draw(network, pos,
45 | node_color=nodecolor,
46 | node_size=30,
47 | width=0.1,
48 | with_labels=False, font_weight='bold')
49 | plt.savefig(name)
50 | return True
51 |
52 | def save_gml(network,outputname,nodecolor):
53 | """
54 | Save the GML file
55 | :return: nothing - should be saved file
56 | """
57 | name = "%s.gml" % outputname
58 | nx.write_gml(network, name)
59 | return True
60 |
61 | def main():
62 | parser = argparse.ArgumentParser()
63 | parser.add_argument("--debug", help="turn on debugging output")
64 | parser.add_argument("--shapefile", help="name of shapefile to import", required=True)
65 | parser.add_argument("--migrationfraction", help="base migration fraction", type=float, required=True, default=0.001)
66 | parser.add_argument("--connectedness", help="k value used to connect nodes in network", type=int, required=True, default=5)
67 | parser.add_argument("--output", help="name of output gml", required=True)
68 | parser.add_argument("--nodecolor", help="color of nodes", required=True, default="blue")
69 |
70 | config = parser.parse_args()
71 |
72 | ## the mean nearest neighbor distance will be used to scale the migration fraction - using the distance between nodes
73 | migration_fraction =config.migrationfraction
74 | networkfile = config.shapefile
75 | output = config.output
76 | nodecolor=config.nodecolor
77 |
78 | k=config.connectedness
79 |
80 | network = nx.read_shp(networkfile)
81 | number=0
82 | node_locations={}
83 | node_name={}
84 |
85 | ## first build a list of locations and names for the new nodes
86 | for ((x,y), data) in network.nodes(data=True):
87 | node_locations[number]=(x,y)
88 | node_name[number]=data['NAME']
89 | number += 1
90 |
91 | nearest_neighbor_distance=[]
92 | new_network=nx.Graph()
93 | for num,xy in list(node_locations.items()):
94 | new_network.add_node(num, x=xy[0], y=xy[1], pos=(xy[0],xy[1]), name=node_name[num])
95 | pos=nx.get_node_attributes(new_network,'pos')
96 |
97 | for num,xy in list(node_locations.items()):
98 |
99 | x1,y1=xy
100 | #print("working on node %s" % num)
101 | node_distances = []
102 | sorted_node_distances = []
103 | # now iterate through to find the n closes networks
104 | ccount=0
105 | for (num2,xy2) in list(node_locations.items()):
106 | x2,y2=xy2
107 | if num != num2:
108 | distance=math.sqrt((x1-x2)**2 + (y1-y2)**2)
109 | node_distances.append(distance)
110 |
111 | sorted_node_distances.append(min(node_distances))
112 | smallest_distance=min(sorted_node_distances)
113 | nearest_neighbor_distance.append(smallest_distance)
114 |
115 | mean_nearest_neighbor_distance=statistics.mean(nearest_neighbor_distance)
116 |
117 | for num, xy in list(node_locations.items()):
118 | # print("working on node %s" % num)
119 | node_distances = {}
120 | # now iterate through to find the n closes networks
121 | ccount = 0
122 | for (num2, xy2) in list(node_locations.items()):
123 | if num != num2:
124 | node_distances[num2] = (math.sqrt((xy[0] - xy2[0]) ** 2 + (xy[1] - xy2[1]) ** 2))
125 | sorted_node_distances = sorted(node_distances.items(), key=operator.itemgetter(1))
126 | list_of_edges_to_add=list(range(0,k))
127 | current_k=k
128 | for e in list_of_edges_to_add:
129 | ne, dist = sorted_node_distances[e]
130 | # network.add_edge(node_locations[num],node_locations[ne], weight=dist )
131 | weight = (dist / mean_nearest_neighbor_distance) * migration_fraction
132 | new_network.add_edge(num, ne, weight=weight)
133 |
134 | print_graph(new_network, pos,nodecolor)
135 | save_graph_plot(new_network,pos,output,k,nodecolor)
136 | save_gml(new_network,output,nodecolor)
137 |
138 | if __name__ == "__main__":
139 | main()
140 |
--------------------------------------------------------------------------------
/networkdrift/utils/utils.py:
--------------------------------------------------------------------------------
1 | from __future__ import division
2 | from collections import defaultdict, OrderedDict
3 | from copy import deepcopy
4 | import simuPOP as sp
5 | import logging as log
6 | import numpy as np
7 | import scipy.stats
8 | import sys
9 | import math
10 | from simuPOP import demography
11 | import os
12 | from collections import Counter
13 |
14 | ### some functions to store stats at each timestep.
15 |
16 | def init_count_traits_in_subpops(pop):
17 | '''
18 | Count traits in the overall population - zero out the dictionary for each loci/allele
19 | combination in the loci/allele trait space
20 | :param pop: the population object - this is passed by simuPop in the PyOperator call.
21 | :return: True
22 | '''
23 | pop.vars()['pop_count']=defaultdict(int)
24 | return True
25 |
26 | def classify(subpops,val):
27 | '''
28 | determine which class each value is in
29 | :param subpops: number of subpops
30 | :param val: the value to check
31 | :return: a list with the category
32 | '''
33 | res = []
34 | if val == 1:
35 | res.append('= 1')
36 | if val == 2:
37 | res.append('= 2')
38 | if val < (int(int(subpops)*.05)):
39 | res.append('< 5%')
40 | if val < (int(int(subpops)*.10)):
41 | res.append('< 10%')
42 | if val < (int(int(subpops)*.2)):
43 | res.append('< 20%')
44 | if val < (int(int(subpops)*.5)):
45 | res.append('< 50%')
46 | return res
47 |
48 | def check_k_and_migration_rates(config):
49 | '''
50 | Check the combination of k and migration rates to see if any combination results in >1.0 total
51 | :param config: the configuration object with the parameters from the command line
52 | :return: output message (if a problem) or True (if no problem
53 | '''
54 | k_values = config.k_values
55 | output_message = ""
56 | count = 0
57 | migs = config.migrationfraction
58 | for k in k_values:
59 | for mig in migs:
60 | if float(k)*float(mig) >= 1.0:
61 | output_message += "k=%s * mig=%4f is greater than 1.0\n" % (k,mig)
62 | count+=1
63 | if count>0:
64 | return output_message
65 | else:
66 | return True
67 |
68 |
69 | def count_traits_in_subpops(pop, param):
70 | '''
71 | Count the number of subpops in which each trait occurs (1-numSubPops)
72 | combination in the loci/allele trait space
73 | :param pop: the population object - this is passed by simuPop in the PyOperator call.
74 | :param param: in this case pass the # of loci
75 | :return: True
76 | '''
77 | (num_loci, numSubPops) = param
78 | sp.stat(pop, haploFreq=range(0,num_loci), vars=['haploFreq_sp', 'haploNum_sp'], subPops=sp.ALL_AVAIL)
79 |
80 | traits_in_subpops = defaultdict(int)
81 |
82 | # now count for all the subpops
83 | for subPop in range(0,numSubPops):
84 | key= list(pop.vars(subPop)['haploNum'].keys())
85 | #traits_n_counts = pop.vars(subPop)['haploNum'][key[0]]
86 | haplotype_count_map= list(pop.vars(subPop)['haploNum'][key[0]].keys())
87 | for loci_allele_tuple in haplotype_count_map:
88 | traits_in_subpops[str(loci_allele_tuple)] +=1
89 |
90 | pop.vars()['pop_count'] = traits_in_subpops
91 |
92 | vals=pop.vars()['pop_count'].values()
93 | ones=twos=fivepercent=tenpercent=twentypercent=fiftypercent=0
94 | for val in vals:
95 | if val == 1:
96 | ones += 1
97 | if val == 2:
98 | twos += 1
99 | if val < (int(int(numSubPops) * .05)):
100 | fivepercent +=1
101 | if val < (int(int(numSubPops) * .10)):
102 | tenpercent +=1
103 | if val < (int(int(numSubPops) * .2)):
104 | twentypercent +=1
105 | if val < (int(int(numSubPops) * .5)):
106 | fiftypercent +=1
107 | pop.vars()['ones'].append(ones)
108 | pop.vars()['twos'].append(twos)
109 | pop.vars()['fivepercent'].append(fivepercent)
110 | pop.vars()['tenpercent'].append(tenpercent)
111 | pop.vars()['twentypercent'].append(twentypercent)
112 | pop.vars()['fiftypercent'].append(fiftypercent)
113 | return True
114 |
115 | def init_acumulators(pop, param):
116 | acumulators = param
117 | for acumulator in acumulators:
118 | if acumulator.endswith('_sp'):
119 | pop.vars()[acumulator] = defaultdict(list)
120 | else:
121 | pop.vars()[acumulator] = []
122 | pop.vars()['allele_frequencies'] = []
123 | pop.vars()['haplotype_frequencies'] = []
124 | pop.vars()['allele_count']=[]
125 | pop.vars()['richness'] = []
126 | pop.vars()['class_freq']=[]
127 | pop.vars()['class_count']=[]
128 | pop.vars()['ones']=[]
129 | pop.vars()['twos']=[]
130 | pop.vars()['fivepercent']=[]
131 | pop.vars()['tenpercent']=[]
132 | pop.vars()['twentypercent']=[]
133 | pop.vars()['fiftypercent'] = []
134 | #pop.vars()['fst_mean']
135 | return True
136 |
137 | def update_acumulator(pop, param):
138 | acumulator, var = param
139 | #for acumulator, var in sorted(param.items()):
140 | log.debug("acumulator: %s var: %s" % (acumulator,var))
141 | if var.endswith('_sp'):
142 | for sp in range(pop.numSubPop()):
143 | pop.vars()[acumulator][sp].append(deepcopy(pop.vars(sp)[var[:-3]]))
144 | else:
145 | pop.vars()[acumulator].append(deepcopy(pop.vars()[var]))
146 |
147 | return True
148 |
149 | def update_richness_acumulator(pop, param):
150 | (acumulator, var) = param
151 | if var.endswith('_sp'):
152 | for sp in range(pop.numSubPop()):
153 | pop.vars()[acumulator][sp].append(len(pop.dvars(sp).alleleFreq[0].values()))
154 | else:
155 | pop.vars()['haplotype_frequencies'].append(len(pop.dvars().haploFreq.values()))
156 | pop.vars()['allele_frequencies'].append(len(pop.dvars().alleleFreq.values()))
157 | pop.vars()['allele_count'].append(len(pop.dvars().alleleNum))
158 | #pop.vars()[acumulator].append(deepcopy(pop.vars()[var]))
159 | return True
160 |
161 | def calculateAlleleAndGenotypeFrequencies(pop, param):
162 | (popsize, num_loci) = param
163 |
164 | sp.stat(pop, haploFreq = range(0, num_loci), vars=['haploFreq', 'haploNum'])
165 | #sim.stat(pop, alleleFreq = sim.ALL_AVAIL)
166 |
167 | keys = list(pop.dvars().haploFreq.keys())
168 |
169 | haplotype_map = pop.dvars().haploFreq[keys[0]]
170 | haplotype_count_map = pop.dvars().haploNum[keys[0]]
171 | num_classes = len(haplotype_map)
172 |
173 | #class_freq = {'-'.join(i[0]) : str(i[1]) for i in haplotype_map.items()}
174 | class_freq = dict()
175 | for k,v in haplotype_map.items():
176 | key = '-'.join(str(x) for x in k)
177 | class_freq[key] = v
178 | #log.debug("class_freq packed: %s", class_freq)
179 |
180 | class_count = dict()
181 | for k,v in haplotype_count_map.items():
182 | key = '-'.join(str(x) for x in k)
183 | class_count[key] = v
184 | pop.vars()['richness'].append(num_classes)
185 | pop.vars()['class_freq'].append(class_freq)
186 | pop.vars()['class_count'].append(class_count)
187 |
188 | return True
189 |
190 | def constructUniformAllelicDistribution(numalleles):
191 | """Constructs a uniform distribution of N alleles in the form of a frequency list.
192 | Args:
193 | numalleles (int): Number of alleles present in the initial population.
194 | Returns:
195 | (list): Array of floats, giving the initial frequency of N alleles.
196 |
197 | """
198 | divisor = 100.0 / numalleles
199 | frac = divisor / 100.0
200 | distribution = [frac] * numalleles
201 | return distribution
202 |
203 | def mean_confidence_interval(data, confidence=0.95):
204 | a = 1.0 * np.array(data)
205 | n = len(a)
206 | m, se = np.mean(a), scipy.stats.sem(a)
207 | h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1)
208 | return m, m-h, m+h
209 |
210 | def setup_output(experiment="test"):
211 | # first create output directories-- should exist already but if not....
212 | path = os.getcwd()
213 | path = path + "/output/"
214 | try:
215 | os.mkdir(path)
216 | except OSError:
217 | print("Creation of the general output directory %s failed. But it might exist. That's okay - carry on. " % path)
218 | else:
219 | print("Successfully created the general output directory %s " % path)
220 | path = path + experiment
221 |
222 | ## now add the specific project path... This should exist (if it does we are writing over old stuff)
223 | try:
224 | os.mkdir(path)
225 | except OSError:
226 | print("Creation of the project output directory %s failed - it might already exist? Quitting... " % path)
227 | sys.exit()
228 | else:
229 | print("Successfully created the project output directory %s " % path)
230 | return path
231 |
232 |
233 | def save_parameters(args, config, output_path):
234 | '''
235 | Save the arguments used to run the experiment into the output director
236 | :param args: the sys.argv string that contains the raw input
237 | :param config: a list of all the possible configuration options
238 | :param output_path: a path to the output directory
239 | :return: True if completed
240 | '''
241 | f = open(output_path + "/parameters.txt", "w+")
242 | f.write("Arguments used: %s\n" % args)
243 | f.write("--experiment: %s\n" % config.experiment)
244 | f.write("--debug: %s\n" % config.debug)
245 | f.write("--reps: %s\n" % config.reps)
246 | f.write("--networkfile: %s\n"% config.networkfile)
247 | f.write("--debug: %s\n" % config.debug)
248 | f.write("--numloci: %s\n" % config.numloci)
249 | f.write("--maxinittraits: %s\n" % config.maxinittraits)
250 | f.write("--innovrate: %s\n" % config.innovrate)
251 | f.write("--simlength: %s\n" % config.simlength)
252 | f.write("--popsize: %s\n" % config.popsize)
253 | f.write("--migrationfraction: %s\n" % config.migrationfraction)
254 | f.write("--seed: %s\n" % config.seed)
255 | f.write("--k_values: %s\n" % config.k_values)
256 | f.write("--sub_pops: %s\n" % config.sub_pops)
257 | f.write("--maxalleles: %s\n" % config.maxalleles)
258 | f.write("--save_figs: %s\n" % config.save_figs)
259 | f.write("--burnintime: %s\n" % config.burnintime)
260 | f.write("--rewiringprob: %s\n" % config.rewiringprob)
261 | f.close()
262 |
263 | return True
264 |
--------------------------------------------------------------------------------
/models/parameter-sweep-for-localization.py:
--------------------------------------------------------------------------------
1 | from __future__ import division
2 | from collections import defaultdict, OrderedDict
3 | from copy import deepcopy
4 | import psutil
5 | import simuOpt
6 | simuOpt.setOptions(alleleType='long', optimized=True, quiet=False)
7 | simuOpt.setOptions(numThreads=psutil.cpu_count())
8 | import math
9 | import seaborn as sns
10 | sns.set_style('white')
11 | import matplotlib.pyplot as plt
12 | import networkdrift.demography.network as network
13 | from networkdrift.utils import utils
14 | import simuPOP as sp
15 | import logging as log
16 | import numpy as np
17 | import scipy.stats
18 | import argparse
19 | import uuid
20 | from time import time
21 | import sys
22 | import os
23 | from collections import defaultdict
24 | import csv
25 |
26 | global config, sim_id, script, cores
27 |
28 | '''
29 | Example use of script.
30 | parameter-sweep-for-localization.py
31 | --experiment paramsweep5 --networkfile smallworld --numloci 1 --maxinittraits 100
32 | --popsize 5000 --migrationfraction 0.0005 0.001 0.0025 0.005
33 | --innovrate 0.00 0.001 0.005 0.01
34 | --k_values 2 25 50 100 125
35 | --rewiringprob 0.001 --sub_pops 200 --maxalleles 10000 --simlength 2005 --reps 5
36 |
37 | example using gml file
38 | parameter-sweep-for-localization.py
39 | --experiment rn-sweep
40 | --networkfile /Users/clipo/Documents/PycharmProjects/network-drift/data/rapa_nui/ahu.gml
41 | --numloci 1 --innovrate 0.00 --maxinittraits 100
42 | --popsize 5000 --migrationfraction 0.000001 0.000005 0.00001 0.00005
43 | --k_values 5 10 25 50 75 100 125 --rewiringprob 0.0
44 | --sub_pops 150 --maxalleles 10000 --simlength 2005 --reps 5
45 | '''
46 |
47 | output=defaultdict(dict)
48 |
49 | def setup(parser):
50 | config = parser.parse_args()
51 | sim_id = uuid.uuid1().urn
52 | script = __file__
53 |
54 | if config.debug == '1':
55 | log.basicConfig(level=log.DEBUG, format='%(asctime)s %(levelname)s: %(message)s')
56 | else:
57 | log.basicConfig(level=log.INFO, format='%(asctime)s %(levelname)s: %(message)s')
58 |
59 | def main():
60 | parser = argparse.ArgumentParser()
61 | parser.add_argument("--experiment", help="provide name for experiment", required=True, type=str, default="test")
62 | parser.add_argument("--debug", help="turn on debugging output")
63 | parser.add_argument("--reps", help="Replicated populations per parameter set", type=int, default=3)
64 | parser.add_argument("--networkfile", help="Name of GML file representing the network model for this simulation",
65 | required=True, type=str, default="smallworld")
66 | parser.add_argument("--numloci", help="Number of loci per individual (use with care)", type=int, required=True, default=1)
67 | parser.add_argument("--maxinittraits", help="Max initial number of traits per locus for initialization", type=int,
68 | required=True, default=50)
69 | parser.add_argument("--innovrate", nargs='+', help="Rate(s) at which innovations occur in population as a per-locus rate", type=float, default=[])
70 | parser.add_argument("--simlength", help="Time at which simulation and sampling end, defaults to 3000 generations",
71 | type=int, default="20")
72 | parser.add_argument("--popsize", help="Initial size of population for each community in the model", type=int, required=True)
73 | parser.add_argument("--migrationfraction", nargs='+', help="Fraction of population that migrates each time step", type=float, required=True, default=[])
74 | parser.add_argument("--seed", type=int, help="Seed for random generators to ensure replicability")
75 | parser.add_argument( "--k_values", nargs='+', type=int, help="list of k-values to explore [e.g., 2 4 20 24]", default=[])
76 | parser.add_argument("--sub_pops", nargs="+", help="Number of sub populations", required=True, default=[])
77 | parser.add_argument("--maxalleles", type=int, help="Maximum number of alleles", default=50)
78 | parser.add_argument("--save_figs", type=bool, help="Save figures or not?", default=False)
79 | parser.add_argument("--burnintime", type=int, help="How long to wait before making measurements? ", default=2000)
80 | parser.add_argument("--rewiringprob", type=float, help="Probability of random rewiring", default=0)
81 |
82 | config = parser.parse_args()
83 |
84 | # setup output directories for writing
85 | output_path = utils.setup_output(config.experiment)
86 |
87 | # check the k and migration rate combinations
88 | check = utils.check_k_and_migration_rates(config)
89 | if check is not True:
90 | print("\nProblem(s):\t %s\n" % check)
91 | print("Please adjust input values for k and/or migration rate and restart.\n ")
92 | sys.exit()
93 | else:
94 | print("\nChecked on the migration and k values -- all looks good!\n")
95 |
96 | # save parameters
97 | utils.save_parameters(str(sys.argv), config, output_path)
98 |
99 | # set up the frequencies for the alleles in each loci. Here assuming a uniform distribution as a starting point
100 | distribution = utils.constructUniformAllelicDistribution(config.maxinittraits)
101 |
102 | # prepare file for output
103 | output_data_file_name = "%s/%s-rare-trait-output.csv" % (output_path, config.experiment)
104 | with open(output_data_file_name, mode='w') as output_file:
105 | output_writer = csv.writer(output_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
106 | output_writer.writerow(["Iteration", "k", "NumSubPops", "Migration", "InnovationRate", "Ones_Mean",
107 | "Ones_95%_Lower", "Ones_95%_Upper", "Twos_Mean", "Twos_95%_Lower", "Twos_95%_Upper", "Richness_Mean",
108 | "Richness_95%_Lower", "Richness_95%_Upper","Fst_Mean","Fst_95%_Lower","Fst_95%_Upper"])
109 | output_file.flush()
110 | subpop_run_values = config.sub_pops
111 | k_run_values = config.k_values
112 | mig_run_values = config.migrationfraction
113 | innov_run_values = config.innovrate
114 |
115 | iteration=-1
116 |
117 | for subpop in subpop_run_values:
118 | if k_run_values == [0]:
119 | k_run_values = [2, int(float(subpop) * .1), int(float(subpop) * .2),
120 | int(float(subpop) * .5), int(float(subpop) * .8),
121 | int(float(subpop) * .9),
122 | int(subpop) - 1]
123 |
124 | for k in k_run_values:
125 | for mig in mig_run_values:
126 | for innov in innov_run_values:
127 | ## let us know whats happening
128 | iteration += 1
129 | print("Now running with subpops: %s k-value: %s mig rate: %4f innov rate: %4f" % (subpop,k,mig,innov))
130 | ## these are lists of things that simuPop will do at different stages
131 | init_ops = OrderedDict()
132 | pre_ops = OrderedDict()
133 | post_ops = OrderedDict()
134 |
135 | # Construct a demographic model
136 | #networkmodel = NetworkModel( networkmodel="/Users/clipo/Documents/PycharmProjects/RapaNuiSim/notebooks/test_graph.gml",
137 | networkmodel = network.NetworkModel( networkmodel=config.networkfile,
138 | simulation_id=config.experiment,
139 | sim_length=config.simlength,
140 | burn_in_time=config.burnintime,
141 | initial_subpop_size=config.popsize,
142 | migrationfraction=mig,
143 | sub_pops=subpop,
144 | connectedness=k, # if 0, then distance decay
145 | save_figs=config.save_figs,
146 | network_iteration=iteration)
147 |
148 | num_pops = networkmodel.get_subpopulation_number()
149 | sub_pop_size = int(config.popsize / num_pops)
150 |
151 | # The regional network model defines both of these, in order to configure an initial population for evolution
152 | # Construct the initial population
153 | pops = sp.Population(size = [sub_pop_size]*num_pops,
154 | subPopNames = str(list(networkmodel.get_subpopulation_names())),
155 | infoFields = 'migrate_to',
156 | ploidy=1,
157 | loci=config.numloci )
158 |
159 | ### now set up the activities
160 | init_ops['acumulators'] = sp.PyOperator(utils.init_acumulators, param=['fst','alleleFreq', 'haploFreq'])
161 | init_ops['subpop_counts'] = sp.PyOperator(utils.init_count_traits_in_subpops)
162 | init_ops['Sex'] = sp.InitSex()
163 | init_ops['Freq'] = sp.InitGenotype(loci=list(range(config.numloci)),freq=distribution)
164 |
165 | post_ops['Innovate'] = sp.KAlleleMutator(k=config.maxalleles, rates=innov, loci=sp.ALL_AVAIL)
166 | post_ops['mig']=sp.Migrator(rate=networkmodel.get_migration_matrix()) #, reps=[3])
167 | post_ops['Stat-fst'] = sp.Stat(structure=sp.ALL_AVAIL)
168 | post_ops['Stat-richness']=sp.Stat(alleleFreq=[0], haploFreq=[0], vars=['alleleFreq','haploFreq','alleleNum', 'genoNum'])
169 | post_ops['fst_acumulation'] = sp.PyOperator(utils.update_acumulator, param=['fst','F_st'])
170 | post_ops['richness_acumulation'] = sp.PyOperator(utils.update_richness_acumulator, param=('alleleFreq', 'Freq of Alleles'))
171 | post_ops['class_richness']=sp.PyOperator(utils.calculateAlleleAndGenotypeFrequencies, param=(config.popsize,config.numloci))
172 | post_ops['count_traits_in_subpops'] = sp.PyOperator(utils.count_traits_in_subpops, param=(config.numloci,num_pops), subPops=sp.ALL_AVAIL)
173 |
174 | mating_scheme = sp.RandomSelection()
175 |
176 | ## go simuPop go! evolve your way to the future!
177 | sim = sp.Simulator(pops, rep=config.reps)
178 | sim.evolve(initOps=list(init_ops.values()), preOps=list(pre_ops.values()), postOps=list(post_ops.values()),
179 | matingScheme=mating_scheme, gen=config.simlength)
180 |
181 | count=0
182 | for pop in sim.populations():
183 | output[count] = deepcopy(pop.dvars())
184 | count+=1
185 |
186 | ones_point_in_time = []
187 | twos_point_in_time = []
188 | richness_point_in_time = []
189 | fst_point_in_time = []
190 |
191 | for n in range(config.reps):
192 | list_of_ones = list(output[n].ones)
193 | list_of_twos = list(output[n].twos)
194 | list_of_richness = list(output[n].richness)
195 | list_of_fst = list(output[n].fst)
196 | ones_point_in_time.append(list_of_ones[2000])
197 | twos_point_in_time.append(list_of_twos[2000])
198 | richness_point_in_time.append(list_of_richness[2000])
199 | fst_point_in_time.append(list_of_fst[2000])
200 |
201 | (ones_ave, ones_min, ones_max) = utils.mean_confidence_interval(ones_point_in_time,
202 | confidence=0.95)
203 | (twos_ave, twos_min, twos_max) = utils.mean_confidence_interval(twos_point_in_time,
204 | confidence=0.95)
205 | (richness_ave, richness_min, richness_max) = utils.mean_confidence_interval(richness_point_in_time,
206 | confidence=0.95)
207 | (fst_ave, fst_min, fst_max) = utils.mean_confidence_interval(fst_point_in_time,
208 | confidence=0.95)
209 |
210 | output_writer.writerow([iteration,k,subpop,mig,innov,ones_ave,ones_min,ones_max,
211 | twos_ave,twos_min,twos_max,richness_ave,richness_min,richness_max,fst_ave,fst_min,fst_max])
212 | output_file.flush()
213 |
214 | if __name__ == "__main__":
215 | main()
216 |
217 |
--------------------------------------------------------------------------------
/models/network-subpop-eval.py:
--------------------------------------------------------------------------------
1 | from __future__ import division
2 | from collections import defaultdict, OrderedDict
3 | from copy import deepcopy
4 | import simuOpt
5 | simuOpt.setOptions(alleleType='long', optimized=True, quiet=False)
6 | import math
7 | import seaborn as sns
8 | sns.set_style('white')
9 | import matplotlib.pyplot as plt
10 | import networkdrift.demography.network as network
11 | from networkdrift.utils import utils
12 | import simuPOP as sp
13 | import logging as log
14 | import numpy as np
15 | import scipy.stats
16 | import argparse
17 | import uuid
18 | from matplotlib import colors as mcolors
19 | from time import time
20 | import sys
21 | import os
22 | from collections import defaultdict
23 |
24 | global config, sim_id, script, cores
25 |
26 | output=defaultdict(dict)
27 |
28 | def setup(parser):
29 | config = parser.parse_args()
30 | sim_id = uuid.uuid1().urn
31 | script = __file__
32 |
33 | if config.debug == '1':
34 | log.basicConfig(level=log.DEBUG, format='%(asctime)s %(levelname)s: %(message)s')
35 | else:
36 | log.basicConfig(level=log.INFO, format='%(asctime)s %(levelname)s: %(message)s')
37 |
38 | def main():
39 | parser = argparse.ArgumentParser()
40 | parser.add_argument("--experiment", help="provide name for experiment", required=True, type=str, default="test")
41 | parser.add_argument("--debug", help="turn on debugging output")
42 | parser.add_argument("--reps", help="Replicated populations per parameter set", type=int, default=1)
43 | parser.add_argument("--networkfile", help="Name of GML file representing the network model for this simulation",
44 | required=True, type=str)
45 | parser.add_argument("--numloci", help="Number of loci per individual", type=int, required=True)
46 | parser.add_argument("--maxinittraits", help="Max initial number of traits per locus for initialization", type=int,
47 | required=True)
48 | parser.add_argument("--innovrate", help="Rate at which innovations occur in population as a per-locus rate", type=float, default=0.001)
49 | parser.add_argument("--simlength", help="Time at which simulation and sampling end, defaults to 3000 generations",
50 | type=int, default="20")
51 | parser.add_argument("--popsize", help="Initial size of population for each community in the model", type=int, required=True)
52 | parser.add_argument("--migrationfraction", nargs='+', help="Fraction of population that migrates each time step",
53 | type=float, required=True, default=[])
54 | parser.add_argument("--seed", type=int, help="Seed for random generators to ensure replicability")
55 | parser.add_argument( "--k_values", nargs='+', type=int, help="list of k-values to explore [e.g., 2 4 20 24", default=[])
56 | parser.add_argument("--sub_pops", nargs='+', help="Number of sub populations", required=True, default=[10])
57 | parser.add_argument("--maxalleles", type=int, help="Maximum number of alleles", default=50)
58 | parser.add_argument("--save_figs", type=bool, help="Save figures or not?", default=True)
59 | parser.add_argument("--burnintime", type=int, help="How long to wait before making measurements? ", default=2000)
60 | parser.add_argument("--rewiringprob", type=float, help="Probability of random rewiring", default=0)
61 |
62 | config = parser.parse_args()
63 |
64 | # check the k and migration rate combinations
65 | for kvalue in config.k_values:
66 | if float(kvalue) * float(config.migrationfraction) >= 1.0:
67 | print("k=%s * mig=%4f is greater than 1.0\n" % (kvalue, config.migrationfraction))
68 | print("Please adjust input values for k and/or migration rate and restart.\n ")
69 | sys.exit()
70 |
71 | # setup output directories for writing
72 | output_path = utils.setup_output(config.experiment)
73 |
74 | # save parameters
75 | utils.save_parameters(str(sys.argv), config, output_path)
76 |
77 | k_run_values=config.k_values
78 | subpop_run_values = config.sub_pops
79 |
80 | ## make sure the k values are less than # of subpops and > 1
81 | for k in k_run_values:
82 | for subnum in subpop_run_values:
83 | if int(k) > int(subnum) or int(k) < 2:
84 | print("k values can not be greater than the number of sub populations. k = %s subpops = %s \n" % (k, subnum))
85 | sys.exit()
86 |
87 | ## initialize the output dictionary
88 | for k in k_run_values:
89 | for sb in subpop_run_values:
90 | output[k][sb]={}
91 |
92 | # set up the frequencies for the alleles in each loci. Here assuming a uniform distribution as a starting point
93 | distribution = utils.constructUniformAllelicDistribution(config.maxinittraits)
94 |
95 | iteration_number=-1
96 | for k in k_run_values:
97 | for subnum in subpop_run_values:
98 | iteration_number += 1
99 | ## these are lists of things that simuPop will do at different stages
100 | init_ops = OrderedDict()
101 | pre_ops = OrderedDict()
102 | post_ops = OrderedDict()
103 |
104 | # Construct a demographic model from a collection of network slices which represent a temporal network
105 | # of changing subpopulations and interaction strengths. This object is Callable, and simply is handed
106 | # to the mating function which applies it during the copying process
107 | #networkmodel = NetworkModel( networkmodel="/Users/clipo/Documents/PycharmProjects/RapaNuiSim/notebooks/test_graph.gml",
108 | networkmodel = network.NetworkModel( networkmodel="smallworld",
109 | simulation_id=config.experiment,
110 | sim_length=config.simlength,
111 | burn_in_time=config.burnintime,
112 | initial_subpop_size=config.popsize,
113 | migrationfraction=config.migrationfraction,
114 | sub_pops=subnum,
115 | connectedness=k, # if 0, then distance decay
116 | save_figs=config.save_figs,
117 | network_iteration=iteration_number)
118 |
119 | num_pops = networkmodel.get_subpopulation_number()
120 | sub_pop_size = int(config.popsize / num_pops)
121 |
122 | # The regional network model defines both of these, in order to configure an initial population for evolution
123 | # Construct the initial population
124 | pops = sp.Population(size = [sub_pop_size]*num_pops,
125 | subPopNames = str(list(networkmodel.get_subpopulation_names())),
126 | infoFields = 'migrate_to',
127 | ploidy=1,
128 | loci=config.numloci )
129 |
130 | ### now set up the activities
131 | init_ops['acumulators'] = sp.PyOperator(utils.init_acumulators, param=['fst','alleleFreq', 'haploFreq'])
132 | init_ops['Sex'] = sp.InitSex()
133 |
134 | init_ops['Freq'] = sp.InitGenotype(loci=list(range(config.numloci)),freq=distribution)
135 |
136 | post_ops['Innovate'] = sp.KAlleleMutator(k=config.maxalleles, rates=config.innovrate, loci=sp.ALL_AVAIL)
137 | post_ops['mig']=sp.Migrator(rate=networkmodel.get_migration_matrix()) #, reps=[3])
138 | #for i, mig in enumerate(migs):
139 | # post_ops['mig-%d' % i] = sp.Migrator(demography.migrIslandRates(mig, num_pops), reps=[i])
140 |
141 | post_ops['Stat-fst'] = sp.Stat(structure=sp.ALL_AVAIL)
142 |
143 | post_ops['Stat-richness']=sp.Stat(alleleFreq=[0], haploFreq=[0], vars=['alleleFreq','haploFreq','alleleNum', 'genoNum'])
144 | post_ops['fst_acumulation'] = sp.PyOperator(utils.update_acumulator, param=['fst','F_st'])
145 | post_ops['richness_acumulation'] = sp.PyOperator(utils.update_richness_acumulator, param=('alleleFreq', 'Freq of Alleles'))
146 | post_ops['class_richness']=sp.PyOperator(utils.calculateAlleleAndGenotypeFrequencies, param=(config.popsize,config.numloci))
147 |
148 | mating_scheme = sp.RandomSelection()
149 | #mating_scheme=sp.RandomSelection(subPopSize=sub_pop_size)
150 |
151 | ## go simuPop go! evolve your way to the future!
152 | sim = sp.Simulator(pops, rep=config.reps)
153 | print("now evolving... k = %s with sub_pops = %s" % (k,subnum))
154 | sim.evolve(initOps=list(init_ops.values()), preOps=list(pre_ops.values()), postOps=list(post_ops.values()),
155 | matingScheme=mating_scheme, gen=config.simlength)
156 |
157 | # now make a figure of the Fst results
158 | fig = plt.figure(figsize=(16, 9))
159 | ax = fig.add_subplot(111)
160 | count=0
161 | for pop in sim.populations():
162 | ax.plot(pop.dvars().fst, label='Replicate: %s' % count)
163 | output[k][subnum][count] = deepcopy(pop.dvars())
164 | count += 1
165 | ax.legend(loc=2)
166 | ax.set_ylabel('FST')
167 | ax.set_xlabel('Generation')
168 | plt.show()
169 |
170 | sum_fig = plt.figure(figsize=(16,9))
171 | ax=sum_fig.add_subplot(111)
172 | iteration=-1
173 | for k in k_run_values:
174 | for subnum in subpop_run_values:
175 | iteration += 1
176 | # only label the first one
177 | for n in range(config.reps):
178 | if n==0:
179 | ax.plot(output[k][subnum][n].fst, color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration], label='k = %s subpops = %s' % (k, subnum))
180 | else:
181 | ax.plot(output[k][subnum][n].fst,
182 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration])
183 | ax.legend(loc=2)
184 | ax.set_ylabel('Fst')
185 | ax.set_xlabel('Generations')
186 | plt.show()
187 | savefilename= output_path + "/sum_fig.png"
188 | sum_fig.savefig(savefilename, bbox_inches='tight')
189 |
190 | rich_fig = plt.figure(figsize=(16,9))
191 | ax=rich_fig.add_subplot(111)
192 | iteration=-1
193 | for k in k_run_values:
194 | for sb in subpop_run_values:
195 | iteration+=1
196 | # only add a label for the first one (not all the replicates)
197 | for n in range(config.reps):
198 | if n==0:
199 | ax.plot(output[k][subnum][n].richness,
200 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration], label='k = %s subpops = %s' % (k, subnum))
201 | else:
202 | ax.plot(output[k][subnum][n].richness,
203 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration])
204 | ax.legend(loc=2)
205 | ax.set_ylabel('Richness')
206 | ax.set_xlabel('Generations')
207 | plt.show()
208 | savefilename = output_path + "/richness.png"
209 | rich_fig.savefig(savefilename, bbox_inches='tight')
210 |
211 | ## output CI for the parameters
212 |
213 | summary_fig = plt.figure(figsize=(16, 9))
214 | ax = summary_fig.add_subplot(111)
215 |
216 | iteration = -1
217 | for k in k_run_values:
218 | for subnum in subpop_run_values:
219 | iteration += 1
220 | CI_average = []
221 | CI_min = []
222 | CI_max = []
223 | for t in range(len(output[k][subnum][0].fst)):
224 | point_in_time = []
225 | for n in range(config.reps):
226 | list_of_points = list(output[k][subnum][n].fst)
227 | point_in_time.append(list_of_points[t])
228 | (ave, min, max) = utils.mean_confidence_interval(point_in_time, confidence=0.95)
229 | CI_average.append(ave)
230 | CI_min.append(min)
231 | CI_max.append(max)
232 | ax.plot(list(CI_average), color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration],label='k = %s subpops = %s' % (k, subnum))
233 | ax.plot(list(CI_min), "--", color="0.5")
234 | ax.plot(list(CI_max), "--", color="0.5")
235 | ax.fill_between(list(CI_average), list(CI_max), list(CI_min), color="None", linestyle="--")
236 | ax.legend(loc=2)
237 | ax.set_ylabel('Fst')
238 | ax.set_xlabel('Generation')
239 | plt.show()
240 | savefilename = output_path + "/summary-ci.png"
241 | summary_fig.savefig(savefilename, bbox_inches='tight')
242 |
243 | ## now the richness graph
244 | richness_sum_fig = plt.figure(figsize=(16, 9))
245 | ax = richness_sum_fig.add_subplot(111)
246 |
247 | iteration=-1
248 | for k in k_run_values:
249 | for subnum in subpop_run_values:
250 | iteration += 1
251 | CI_average = []
252 | CI_min = []
253 | CI_max = []
254 | for t in range(len(output[k][subnum][0].richness)):
255 | point_in_time = []
256 | for n in range(config.reps):
257 | list_of_points = list(output[k][subnum][n].richness)
258 | point_in_time.append(list_of_points[t])
259 | (ave, min, max) = utils.mean_confidence_interval(point_in_time, confidence=0.95)
260 | CI_average.append(ave)
261 | CI_min.append(min)
262 | CI_max.append(max)
263 | ax.plot(list(CI_average), color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration],label='k = %s subpops = %s' % (k, subnum))
264 | ax.plot(list(CI_min), "--", color="0.5")
265 | ax.plot(list(CI_max), "--", color="0.5")
266 | ax.fill_between(list(CI_average), list(CI_max), list(CI_min), color="None", linestyle="--")
267 | ax.legend(loc=2)
268 | ax.set_ylabel('Richness')
269 | ax.set_xlabel('Generation')
270 | plt.show()
271 | savefilename = output_path + "/richness-ci.png"
272 | richness_sum_fig.savefig(savefilename, bbox_inches='tight')
273 |
274 | if __name__ == "__main__":
275 | main()
276 |
277 |
--------------------------------------------------------------------------------
/models/network-k-eval.py:
--------------------------------------------------------------------------------
1 | from __future__ import division
2 | from collections import defaultdict, OrderedDict
3 | from copy import deepcopy
4 | import simuOpt
5 | simuOpt.setOptions(alleleType='long', optimized=True, quiet=False)
6 | import math
7 | import seaborn as sns
8 | sns.set_style('white')
9 | import matplotlib.pyplot as plt
10 | import networkdrift.demography.network as network
11 | from networkdrift.utils import utils
12 | import simuPOP as sp
13 | import logging as log
14 | import numpy as np
15 | import scipy.stats
16 | import argparse
17 | import uuid
18 | from matplotlib import colors as mcolors
19 | from time import time
20 | import sys
21 | import os
22 | from collections import defaultdict
23 |
24 | global config, sim_id, script, cores
25 |
26 | output=defaultdict(dict)
27 |
28 | def setup(parser):
29 | config = parser.parse_args()
30 | sim_id = uuid.uuid1().urn
31 | script = __file__
32 |
33 | if config.debug == '1':
34 | log.basicConfig(level=log.DEBUG, format='%(asctime)s %(levelname)s: %(message)s')
35 | else:
36 | log.basicConfig(level=log.INFO, format='%(asctime)s %(levelname)s: %(message)s')
37 |
38 | def main():
39 | parser = argparse.ArgumentParser()
40 | parser.add_argument("--experiment", help="provide name for experiment", required=True, type=str, default="test")
41 | parser.add_argument("--debug", help="turn on debugging output")
42 | parser.add_argument("--reps", help="Replicated populations per parameter set", type=int, default=1)
43 | parser.add_argument("--networkfile", help="Name of GML file representing the network model for this simulation",
44 | required=True, type=str)
45 | parser.add_argument("--numloci", help="Number of loci per individual", type=int, required=True)
46 | parser.add_argument("--maxinittraits", help="Max initial number of traits per locus for initialization", type=int,
47 | required=True)
48 | parser.add_argument("--innovrate", help="Rate at which innovations occur in population as a per-locus rate", type=float, default=0.001)
49 | parser.add_argument("--simlength", help="Time at which simulation and sampling end, defaults to 3000 generations",
50 | type=int, default="20")
51 | parser.add_argument("--popsize", help="Initial size of population for each community in the model", type=int, required=True)
52 | parser.add_argument("--migrationfraction", help="Fraction of population that migrates each time step",
53 | type=float, required=True, default=0.0001)
54 | parser.add_argument("--seed", type=int, help="Seed for random generators to ensure replicability")
55 | parser.add_argument( "--k_values", nargs='+', type=int, help="list of k-values to explore [e.g., 2 4 20 24", default=[])
56 | parser.add_argument("--sub_pops", type=int, help="Number of sub populations", required=True, default=10)
57 | parser.add_argument("--maxalleles", type=int, help="Maximum number of alleles", default=50)
58 | parser.add_argument("--save_figs", type=bool, help="Save figures or not?", default=True)
59 | parser.add_argument("--burnintime", type=int, help="How long to wait before making measurements? ", default=2000)
60 | parser.add_argument("--rewiringprob", type=float, help="Probability of random rewiring", default=0)
61 |
62 | config = parser.parse_args()
63 |
64 | # check the k and migration rate combinations
65 | for kvalue in list(config.k_values):
66 | if float(kvalue) * float(config.migrationfraction) >= 1.0:
67 | print("k=%s * mig=%4f is greater than 1.0\n" % (kvalue, config.migrationfraction))
68 | print("Please adjust input values for k and/or migration rate and restart.\n ")
69 | sys.exit()
70 |
71 | # setup output directories for writing
72 | output_path = utils.setup_output(config.experiment)
73 |
74 | # save parameters
75 | utils.save_parameters(str(sys.argv), config, output_path)
76 |
77 | run_param=config.k_values
78 |
79 |
80 | ## initialize the output dictionary
81 | for k in run_param:
82 | output[k]={}
83 | if k >= config.sub_pops:
84 | print("k values cannot be greater than the number of sub pops.\n")
85 | sys.exit()
86 |
87 | # set up the frequencies for the alleles in each loci. Here assuming a uniform distribution as a starting point
88 | distribution = utils.constructUniformAllelicDistribution(config.maxinittraits)
89 |
90 | iteration_number=-1
91 | for param_value in run_param:
92 | iteration_number += 1
93 | ## these are lists of things that simuPop will do at different stages
94 | init_ops = OrderedDict()
95 | pre_ops = OrderedDict()
96 | post_ops = OrderedDict()
97 |
98 | # Construct a demographic model from a collection of network slices which represent a temporal network
99 | # of changing subpopulations and interaction strengths. This object is Callable, and simply is handed
100 | # to the mating function which applies it during the copying process
101 | #networkmodel = NetworkModel( networkmodel="/Users/clipo/Documents/PycharmProjects/RapaNuiSim/notebooks/test_graph.gml",
102 | networkmodel = network.NetworkModel( networkmodel=config.networkfile,
103 | simulation_id=config.experiment,
104 | sim_length=config.simlength,
105 | burn_in_time=config.burnintime,
106 | initial_subpop_size=config.popsize,
107 | migrationfraction=config.migrationfraction,
108 | sub_pops=int(config.sub_pops),
109 | connectedness=param_value, # if 0, then distance decay
110 | save_figs=config.save_figs,
111 | network_iteration=iteration_number,
112 | output_path=output_path)
113 |
114 | num_pops = networkmodel.get_subpopulation_number()
115 | if param_value >= num_pops:
116 | print("k values cannot be greater or equal to the number of sub pops.\n")
117 | sys.exit()
118 | sub_pop_size = int(config.popsize / num_pops)
119 |
120 | # The regional network model defines both of these, in order to configure an initial population for evolution
121 | # Construct the initial population
122 | pops = sp.Population(size = [sub_pop_size]*num_pops,
123 | subPopNames = str(list(networkmodel.get_subpopulation_names())),
124 | infoFields = 'migrate_to',
125 | ploidy=1,
126 | loci=config.numloci )
127 |
128 | ### now set up the activities
129 | init_ops['acumulators'] = sp.PyOperator(utils.init_acumulators, param=['fst','alleleFreq', 'haploFreq'])
130 | init_ops['subpop_counts'] = sp.PyOperator(utils.init_count_traits_in_subpops)
131 | init_ops['Sex'] = sp.InitSex()
132 | init_ops['Freq'] = sp.InitGenotype(loci=list(range(config.numloci)),freq=distribution)
133 |
134 | post_ops['Innovate'] = sp.KAlleleMutator(k=config.maxalleles, rates=config.innovrate, loci=sp.ALL_AVAIL)
135 | post_ops['mig']=sp.Migrator(rate=networkmodel.get_migration_matrix()) #, reps=[3])
136 | post_ops['Stat-fst'] = sp.Stat(structure=sp.ALL_AVAIL)
137 | post_ops['Stat-richness']=sp.Stat(alleleFreq=[0], haploFreq=[0], vars=['alleleFreq','haploFreq','alleleNum', 'genoNum'])
138 | post_ops['fst_acumulation'] = sp.PyOperator(utils.update_acumulator, param=['fst','F_st'])
139 | post_ops['richness_acumulation'] = sp.PyOperator(utils.update_richness_acumulator, param=('alleleFreq', 'Freq of Alleles'))
140 | post_ops['class_richness']=sp.PyOperator(utils.calculateAlleleAndGenotypeFrequencies, param=(config.popsize,config.numloci))
141 | post_ops['count_traits_in_subpops'] = sp.PyOperator(utils.count_traits_in_subpops, param=(config.numloci,num_pops), subPops=sp.ALL_AVAIL)
142 |
143 | mating_scheme = sp.RandomSelection()
144 |
145 | ## go simuPop go! evolve your way to the future!
146 | sim = sp.Simulator(pops, rep=config.reps)
147 | print("now evolving... k= %s" % param_value)
148 | sim.evolve(initOps=list(init_ops.values()), preOps=list(pre_ops.values()), postOps=list(post_ops.values()),
149 | matingScheme=mating_scheme, gen=config.simlength)
150 |
151 | # now make a figure of the Fst results
152 | fig = plt.figure(figsize=(16, 9))
153 | ax = fig.add_subplot(111)
154 | count=0
155 | for pop in sim.populations():
156 | ax.plot(pop.dvars().fst, label='Replicate: %s' % count)
157 | output[param_value][count] = deepcopy(pop.dvars())
158 | count += 1
159 | ax.legend(loc=2)
160 | ax.set_ylabel('FST')
161 | ax.set_xlabel('Generation')
162 | plt.show()
163 |
164 | ## draw traits in 1s or 2s of the subpops
165 | subpop_fig = plt.figure(figsize=(16,9))
166 | ax=subpop_fig.add_subplot(111)
167 | iteration = -1
168 | for k in run_param:
169 | iteration += 1
170 | # only label the first one
171 | for n in range(config.reps):
172 | if n == 0:
173 | #print(output[k][n].ones)
174 | ax.plot(output[k][n].ones,
175 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration],
176 | label='k = %s - traits in just one subpopulation' % k)
177 | ax.plot(output[k][n].twos, "--",
178 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration],
179 | label='k = %s - traits in just two or fewer subpopulations'% k)
180 | else:
181 | ax.plot(output[k][n].ones,
182 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration])
183 | ax.plot(output[k][n].twos,"--",
184 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration])
185 | ax.legend(loc=2)
186 | ax.set_ylabel('Numbers of Traits')
187 | ax.set_xlabel('Generations')
188 | plt.show()
189 | savefilename = output_path + "/subpop_fig.svg"
190 | subpop_fig.savefig(savefilename, bbox_inches='tight')
191 |
192 | sum_fig = plt.figure(figsize=(16,9))
193 | ax=sum_fig.add_subplot(111)
194 | iteration=-1
195 | for k in run_param:
196 | iteration += 1
197 | # only label the first one
198 | for n in range(config.reps):
199 | if n==0:
200 | ax.plot(output[k][n].fst, color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration], label='k = %s' % k)
201 | else:
202 | ax.plot(output[k][n].fst,
203 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration])
204 | ax.legend(loc=2)
205 | ax.set_ylabel('Fst')
206 | ax.set_xlabel('Generations')
207 | plt.show()
208 | savefilename= output_path + "/sum_fig.svg"
209 | sum_fig.savefig(savefilename, bbox_inches='tight')
210 |
211 | rich_fig = plt.figure(figsize=(16,9))
212 | ax=rich_fig.add_subplot(111)
213 | iteration=-1
214 | for k in run_param:
215 | iteration+=1
216 | # only add a label for the first one (not all the replicates)
217 | for n in range(config.reps):
218 | if n==0:
219 | ax.plot(output[k][n].richness,
220 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration], label = 'k = %s' % k)
221 | else:
222 | ax.plot(output[k][n].richness,
223 | color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration])
224 | ax.legend(loc=2)
225 | ax.set_ylabel('Richness')
226 | ax.set_xlabel('Generations')
227 | plt.show()
228 | savefilename = output_path + "/richness.svg"
229 | rich_fig.savefig(savefilename, bbox_inches='tight')
230 |
231 | ## output CI for the parameters
232 |
233 | summary_fig = plt.figure(figsize=(16, 9))
234 | ax = summary_fig.add_subplot(111)
235 |
236 | iteration = -1
237 | for k in run_param:
238 | iteration += 1
239 | CI_average = []
240 | CI_min = []
241 | CI_max = []
242 | for t in range(len(output[k][0].fst)):
243 | point_in_time = []
244 | for n in range(config.reps):
245 | list_of_points = list(output[k][n].fst)
246 | point_in_time.append(list_of_points[t])
247 | (ave, min, max) = utils.mean_confidence_interval(point_in_time, confidence=0.95)
248 | CI_average.append(ave)
249 | CI_min.append(min)
250 | CI_max.append(max)
251 | ax.plot(list(CI_average), color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration],label='k = %s' % k)
252 | ax.plot(list(CI_min), "--", color="0.5")
253 | ax.plot(list(CI_max), "--", color="0.5")
254 | ax.fill_between(list(CI_average), list(CI_max), list(CI_min), color="None", linestyle="--")
255 | ax.legend(loc=2)
256 | ax.set_ylabel('Fst')
257 | ax.set_xlabel('Generation')
258 | plt.show()
259 | savefilename = output_path + "/summary-ci.svg"
260 | summary_fig.savefig(savefilename, bbox_inches='tight')
261 |
262 | ## now the richness graph
263 | richness_sum_fig = plt.figure(figsize=(16, 9))
264 | ax = richness_sum_fig.add_subplot(111)
265 |
266 | iteration=-1
267 | for k in run_param:
268 | iteration += 1
269 | CI_average = []
270 | CI_min = []
271 | CI_max = []
272 | for t in range(len(output[k][0].richness)):
273 | point_in_time = []
274 | for n in range(config.reps):
275 | list_of_points = list(output[k][n].richness)
276 | point_in_time.append(list_of_points[t])
277 | (ave, min, max) = utils.mean_confidence_interval(point_in_time, confidence=0.95)
278 | CI_average.append(ave)
279 | CI_min.append(min)
280 | CI_max.append(max)
281 | ax.plot(list(CI_average), color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[iteration],label='k = %s' % k)
282 | ax.plot(list(CI_min), "--", color="0.5")
283 | ax.plot(list(CI_max), "--", color="0.5")
284 | ax.fill_between(list(CI_average), list(CI_max), list(CI_min), color="None", linestyle="--")
285 | ax.legend(loc=2)
286 | ax.set_ylabel('Richness')
287 | ax.set_xlabel('Generation')
288 | plt.show()
289 | savefilename = output_path + "/richness-ci.svg"
290 | richness_sum_fig.savefig(savefilename, bbox_inches='tight')
291 |
292 | if __name__ == "__main__":
293 | main()
294 |
295 |
--------------------------------------------------------------------------------
/networkdrift/demography/network.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) 2015. Mark E. Madsen
3 | #
4 | # This work is licensed under the terms of the Apache Software License, Version 2.0. See the file LICENSE for details.
5 |
6 | """
7 | Description here
8 |
9 | """
10 | import networkx as nx
11 | import numpy as np
12 | import re
13 | import math
14 | import simuPOP as sim
15 | import logging as log
16 | import matplotlib.pyplot as plt
17 | from matplotlib import colors as mcolors
18 | from itertools import product
19 | import sys
20 | from sklearn.preprocessing import normalize
21 | import networkx as nx
22 | import matplotlib.pyplot as plt
23 | from collections import defaultdict
24 | from itertools import permutations
25 | import operator
26 | import math
27 | import statistics
28 |
29 | class NetworkModel(object):
30 | """
31 | NetworkModel implements a full "demographic model" in simuPOP terms,
32 | that is, it manages the size and existence of subpopulations, and the
33 | migration matrix between them. The basic data for this model is derived
34 | by the creation of a random small-world NetworkX network in the (watts_strogatz_graph) or
35 | the creation of a network from a GML file.
36 | The network edges represent a set of subpopulations
37 | with unique ID's, and edges between them which are weighted. The
38 | weights may be determined by any underlying model (e.g., distance,
39 | social interaction hierarchy, etc), but need to be interpreted here
40 | purely as the probability of individuals moving between two subpopulations,
41 | since that is the implementation of interaction.
42 | """
43 |
44 | def __init__(self,
45 | networkmodel="smallworld",
46 | simulation_id=None,
47 | sim_length=1000,
48 | burn_in_time=0,
49 | initial_subpop_size=500,
50 | migrationfraction=0.01,
51 | sub_pops=10,
52 | connectedness=2,
53 | rewiring_prob=0.0,
54 | save_figs=True,
55 | network_iteration=1,
56 | xy=None,
57 | output_path="test"):
58 | """
59 | :param networkmodel: Name of GML file
60 | :param sim_length: Number of generations to run the simulation
61 | :return:
62 | """
63 | # BaseMetapopulationModel.__init__(self, numGens = sim_length, initSize = initial_size, infoFields = info_fields,
64 | # ops = ops, sub_pops = num_subpops, connectedness = connectedness, xy=xy rewiring_prob=rewiring_prob,
65 | # save_figs= boolean, iteration=number)
66 | self.networkmodel = networkmodel # default is small world - else GML file location
67 | self.sim_length = sim_length
68 | self.info_fields = 'migrate_to'
69 | self.sim_id = simulation_id
70 | self.burn_in_time = burn_in_time
71 | self.init_subpop_size = initial_subpop_size
72 | self.migration_fraction = migrationfraction
73 | self.connectedness = connectedness # default of 3
74 | self.sub_pops = sub_pops # default of 5
75 | self.rewiring_prob = rewiring_prob # default of 0.0
76 | self._cached_migration_matrix = None
77 | self.subpopulation_names = []
78 | self.save_figs=save_figs
79 | self.network_iteration=network_iteration
80 | self.xy=[]
81 | self.output_path = output_path
82 |
83 | # Parse the GML files and create a list of NetworkX objects
84 | self._parse_network_model()
85 |
86 | # Determine the initial population configuration
87 | self._calculate_initial_population_configuration()
88 |
89 | # prime the migration matrix
90 | if self.connectedness==self.sub_pops:
91 | self._cached_migration_matrix=self._spatialMigrRates()
92 | log.debug(self._cached_migration_matrix)
93 | self.connectedness=self.sub_pops-1
94 | elif ".gml" in self.networkmodel:
95 | self._cached_migration_matrix=self._calculate_migration_matrix_from_gml()
96 | log.debug(self._cached_migration_matrix)
97 | else:
98 | ## used the fixed migration function for now - which determines each edge
99 | ## note that k * migration rate must be < 1.0
100 | self._cached_migration_matrix = self._calculate_fixed_migration_matrix()
101 | #self._cached_migration_matrix = self._calculate_migration_matrix()
102 |
103 | ############### Private Initialization Methods ###############
104 |
105 | def _parse_network_model(self):
106 | """
107 | Given a file, read the GML files (format: .gml)
108 | and construct a NetworkX networkmodel from the GML file
109 | """
110 | if self.networkmodel == "smallworld":
111 | log.debug("Creating small world Watts-Strogatz network with %s nodes and %s connections " % (
112 | self.sub_pops, self.connectedness))
113 | k=self.connectedness
114 | if k == self.sub_pops:
115 | k=k-1
116 | network = nx.watts_strogatz_graph(int(self.sub_pops), k, self.rewiring_prob)
117 | self.pos = nx.spring_layout(network, iterations=25)
118 | log.debug("network nodes: %s", '|'.join(sorted(str(list(network.nodes)))))
119 | self.network = network
120 | self.xy=self._set_xy_coordinates()
121 | elif ".gml" in self.networkmodel:
122 | log.debug("Opening GML file %s:", self.networkmodel)
123 | network = nx.read_gml(self.networkmodel)
124 | log.debug("network nodes: %s", '|'.join(sorted(list(network.nodes()))))
125 | self.network = self._create_network_edges_from_k_value(network)
126 | else:
127 | print("There's been a problem - we haven't created the network. Bailing out!\n")
128 | sys.exit()
129 |
130 | if self.save_figs == True:
131 | self.print_graph()
132 | self.save_graph()
133 |
134 | def _calculate_initial_population_configuration(self):
135 | # num subpops is just the number of vertices in the first graph slice.
136 | # first_time = min(self.times)
137 | network = self.network
138 | self.sub_pops = network.number_of_nodes()
139 | log.debug("Number of initial subpopulations: %s", self.sub_pops)
140 | log.debug("list of nodes: %s", list(network.nodes(data=True)))
141 | # subpoplation names - have to switch them to plain strings from unicode or simuPOP won't use them as subpop names
142 | self.subpopulation_names = list(network.nodes)
143 |
144 | log.debug("calc_init_config: subpopulation names: %s", self.subpopulation_names)
145 |
146 | ############### Private Methods for Call() Interface ###############
147 |
148 | def _get_node_label(self, g, id):
149 | return g.node[id]["label"].encode('utf-8', 'ignore')
150 |
151 | def _get_id_for_subpop_name(self, pop, name):
152 | return pop.subPopByName(name)
153 |
154 | def _get_node_parent(self, g, id):
155 | return g.node[id]["parent_node"].encode('utf-8', 'ignore')
156 |
157 | def _get_subpop_idname_map(self, pop):
158 | names = pop.subPopNames()
159 | name_id_map = dict()
160 | for name in names:
161 | id = pop.subPopByName(name)
162 | name_id_map[id] = name
163 | return name_id_map
164 |
165 | def _calculate_fixed_migration_matrix(self):
166 | for (node1, node2, data) in self.network.edges(data=True):
167 | self.network.add_edge(node1, node2, weight=self.migration_fraction)
168 | g_mat = nx.to_numpy_matrix(self.network)
169 | #print("normed_matrix: ", g_mat)
170 | return g_mat.tolist()
171 |
172 | def _calculate_migration_matrix_from_gml(self):
173 | g_mat = nx.to_numpy_matrix(self.network).astype(np.float)
174 | return g_mat.tolist()
175 |
176 | def _calculate_migration_matrix(self):
177 | g_mat = nx.to_numpy_matrix(self.network, weight=self.migration_fraction).astype(np.float)
178 | #print("g_mat: ", g_mat)
179 | # get the column totals
180 | rtot = np.sum(g_mat, axis=1)
181 | scaled = (g_mat / rtot) * self.migration_fraction
182 | diag = np.eye(np.shape(g_mat)[0]) * (1.0 - self.migration_fraction)
183 | g_mat_scaled = diag + scaled
184 | log.debug("scaled migration matrix: %s", g_mat_scaled.tolist())
185 | #print("g_mat_scaled: ", g_mat_scaled)
186 | return g_mat_scaled.tolist()
187 |
188 | def _spatialMigrRates(self):
189 | '''
190 | Return a migration matrix where migration rates between two
191 | subpopulations vary according to Euclidean distance between them.
192 |
193 | xy
194 | A list of (x,y) location for each subpopulation.
195 |
196 | r
197 | Migrate rate between two subpopulations is exp(-r*d_ij) where
198 | d_ij is the Euclidean distance between subpopulations i and j.
199 | '''
200 | r=self.migration_fraction
201 | xy=list(self.xy)
202 | #print(xy)
203 | nSubPop = self.sub_pops
204 | rate = []
205 | for i in range(nSubPop):
206 | rate.append([])
207 | for j in range(nSubPop):
208 | if i == j:
209 | rate[-1].append(0)
210 | continue
211 | d_ij = math.sqrt((xy[i][0] - xy[j][0]) ** 2 + (xy[i][1] - xy[j][1]) ** 2)
212 | rate[-1].append(math.exp(-1 * r * d_ij))
213 | self._cached_migration_matrix=rate
214 | return rate
215 |
216 | def _set_xy_coordinates(self):
217 | radius=100
218 | pts=self.sub_pops
219 | for x, y in product(range(0, int(radius) + 1, int(360 / pts)), repeat=2):
220 | if x ** 2 + y ** 2 <= radius ** 2:
221 | yield from set(((x, y), (x, -y), (-x, y), (-x, -y),))
222 |
223 | def _create_network_edges_from_k_value(self,network):
224 | new_network=nx.Graph()
225 | nearest_neighbor_distance = []
226 | number = 0
227 | node_pos = {}
228 | node_name = {}
229 | ## first build a list of locations and names for the new nodes
230 | for (num, data) in network.nodes(data=True):
231 | node_pos[num] = data['pos']
232 | node_name[num] = data['name']
233 |
234 | for num, xy in list(node_pos.items()):
235 | new_network.add_node(num, x=xy[0], y=xy[1], pos=(xy[0], xy[1]), name=node_name[num])
236 |
237 | self.pos = nx.get_node_attributes(new_network, 'pos')
238 |
239 | for num, xy in list(node_pos.items()):
240 | x1, y1 = xy
241 | # print("working on node %s" % num)
242 | node_distances = []
243 | sorted_node_distances = []
244 | # now iterate through to find the n closest networks
245 | ccount = 0
246 | for (num2, xy2) in list(node_pos.items()):
247 | x2, y2 = xy2
248 | if num != num2:
249 | distance = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
250 | node_distances.append(distance)
251 |
252 | sorted_node_distances.append(min(node_distances))
253 | smallest_distance = min(sorted_node_distances)
254 | nearest_neighbor_distance.append(smallest_distance)
255 |
256 | mean_nearest_neighbor_distance = statistics.mean(nearest_neighbor_distance)
257 |
258 | # now add the connections based on the degree of k specified
259 | for num,(x,y) in list(node_pos.items()):
260 | # print("working on node %s" % num)
261 | node_distances = {}
262 | # now iterate through to find the n closes networks
263 | ccount = 0
264 | for (num2, (x2,y2)) in list(node_pos.items()):
265 | if num != num2:
266 | node_distances[num2] = (math.sqrt((x - x2) ** 2 + (y - y2) ** 2))
267 | sorted_node_distances = sorted(node_distances.items(), key=operator.itemgetter(1))
268 | list_of_edges_to_add = list(range(0, self.connectedness))
269 | current_k = self.connectedness
270 | # note: we dont want to add edges if they are already there
271 | for e in list_of_edges_to_add:
272 | ne, dist = sorted_node_distances[e]
273 | # network.add_edge(node_locations[num],node_locations[ne], weight=dist )
274 | weight=(dist / mean_nearest_neighbor_distance) * self.migration_fraction
275 | new_network.add_edge(num, ne, weight=weight)
276 |
277 | return new_network
278 |
279 | ###################### Public API #####################
280 |
281 | def get_info_fields(self):
282 | return self.info_fields
283 |
284 | def get_connectedness(self):
285 | return self.connectedness
286 |
287 | def get_initial_size(self):
288 | return [self.init_subpop_size] * self.sub_pops
289 |
290 | def get_subpopulation_names(self):
291 | return str(list(self.subpopulation_names))
292 |
293 | def get_subpopulation_sizes(self):
294 | return self.subpop_sizes
295 |
296 | def get_subpopulation_number(self):
297 | return len(self.subpopulation_names)
298 |
299 | def get_migration_matrix(self):
300 | return self._cached_migration_matrix
301 |
302 | def print_graph(self):
303 | """
304 | Show us the graph of the network.
305 | :return: nothing - should be a matplotlib plot
306 | """
307 | plt.subplot(111)
308 | nx.draw(self.network,self.pos,node_color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[self.network_iteration], with_labels=True, font_weight='bold')
309 | plt.show()
310 |
311 | def save_graph(self):
312 | """
313 | Save the graph of the network.
314 | :return: nothing - should be saved file
315 | """
316 | name = "%s/k-%s.svg" % (self.output_path,self.connectedness)
317 | nx.draw(self.network,self.pos,node_color=list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())[self.network_iteration], with_labels=True, font_weight='bold')
318 | plt.savefig(name)
319 |
320 | def __call__(self, pop):
321 | """
322 | Main public interface to this demography model. When the model object is called in every time step,
323 | this method creates a new migration matrix.
324 |
325 | After migration, the stat function is called to inventory the subpopulation sizes, which are then
326 | returned since they're handed to the RandomSelection mating operator.
327 |
328 | If a new network slice is not active, the migration matrix from the previous step is applied again,
329 | and the new subpopulation sizes are returns to the RandomSelection mating operator as before.
330 |
331 | :return: A list of the subpopulation sizes for each subpopulation
332 | """
333 | if 'gen' not in pop.vars():
334 | gen = 0
335 | else:
336 | gen = pop.dvars().gen
337 |
338 | ######### Do the per tick processing ##########
339 |
340 | log.debug("========= Processing network =============")
341 | # self._dbg_slice_pop_start(pop,gen)
342 |
343 | # update the migration matrix
344 | self._cached_migration_matrix = self._calculate_migration_matrix(gen)
345 |
346 | sim.migrate(pop, self._cached_migration_matrix)
347 | sim.stat(pop, popSize=True)
348 | # cache the new subpopulation names and sizes for debug and logging purposes
349 | # before returning them to the calling function
350 | self.subpopulation_names = sorted(str(list(pop.subPopNames())))
351 | self.subpop_sizes = pop.subPopSizes()
352 | #print(self.subpop_sizes)
353 | return pop.subPopSizes()
354 |
355 |
--------------------------------------------------------------------------------
/testdata/test-network.gml:
--------------------------------------------------------------------------------
1 | graph [
2 | name "(complete_graph(8))_with_int_labels"
3 | node [
4 | id 0
5 | label "assemblage-167-400"
6 | appears_in_slice "1"
7 | ycoord "400"
8 | level "None"
9 | cluster_id "0"
10 | xcoord "167"
11 | parent_node "initial"
12 | ]
13 | node [
14 | id 1
15 | label "assemblage-176-392"
16 | appears_in_slice "1"
17 | ycoord "392"
18 | level "None"
19 | cluster_id "0"
20 | xcoord "176"
21 | parent_node "initial"
22 | ]
23 | node [
24 | id 2
25 | label "assemblage-165-402"
26 | appears_in_slice "1"
27 | ycoord "402"
28 | level "None"
29 | cluster_id "0"
30 | xcoord "165"
31 | parent_node "initial"
32 | ]
33 | node [
34 | id 3
35 | label "assemblage-172-374"
36 | appears_in_slice "1"
37 | ycoord "374"
38 | level "None"
39 | cluster_id "0"
40 | xcoord "172"
41 | parent_node "initial"
42 | ]
43 | node [
44 | id 4
45 | label "assemblage-182-393"
46 | appears_in_slice "1"
47 | ycoord "393"
48 | level "None"
49 | cluster_id "0"
50 | xcoord "182"
51 | parent_node "initial"
52 | ]
53 | node [
54 | id 5
55 | label "assemblage-164-395"
56 | appears_in_slice "1"
57 | ycoord "395"
58 | level "None"
59 | cluster_id "0"
60 | xcoord "164"
61 | parent_node "initial"
62 | ]
63 | node [
64 | id 6
65 | label "assemblage-162-396"
66 | appears_in_slice "1"
67 | ycoord "396"
68 | level "None"
69 | cluster_id "0"
70 | xcoord "162"
71 | parent_node "initial"
72 | ]
73 | node [
74 | id 7
75 | label "assemblage-185-383"
76 | appears_in_slice "1"
77 | ycoord "383"
78 | level "None"
79 | cluster_id "0"
80 | xcoord "185"
81 | parent_node "initial"
82 | ]
83 | node [
84 | id 8
85 | label "assemblage-396-320"
86 | appears_in_slice "1"
87 | ycoord "320"
88 | level "None"
89 | cluster_id "1"
90 | xcoord "396"
91 | parent_node "initial"
92 | ]
93 | node [
94 | id 9
95 | label "assemblage-398-306"
96 | appears_in_slice "1"
97 | ycoord "306"
98 | level "None"
99 | cluster_id "1"
100 | xcoord "398"
101 | parent_node "initial"
102 | ]
103 | node [
104 | id 10
105 | label "assemblage-411-290"
106 | appears_in_slice "1"
107 | ycoord "290"
108 | level "None"
109 | cluster_id "1"
110 | xcoord "411"
111 | parent_node "initial"
112 | ]
113 | node [
114 | id 11
115 | label "assemblage-404-314"
116 | appears_in_slice "1"
117 | ycoord "314"
118 | level "None"
119 | cluster_id "1"
120 | xcoord "404"
121 | parent_node "initial"
122 | ]
123 | node [
124 | id 12
125 | label "assemblage-422-296"
126 | appears_in_slice "1"
127 | ycoord "296"
128 | level "None"
129 | cluster_id "1"
130 | xcoord "422"
131 | parent_node "initial"
132 | ]
133 | node [
134 | id 13
135 | label "assemblage-401-292"
136 | appears_in_slice "1"
137 | ycoord "292"
138 | level "None"
139 | cluster_id "1"
140 | xcoord "401"
141 | parent_node "initial"
142 | ]
143 | node [
144 | id 14
145 | label "assemblage-418-297"
146 | appears_in_slice "1"
147 | ycoord "297"
148 | level "None"
149 | cluster_id "1"
150 | xcoord "418"
151 | parent_node "initial"
152 | ]
153 | node [
154 | id 15
155 | label "assemblage-419-328"
156 | appears_in_slice "1"
157 | ycoord "328"
158 | level "None"
159 | cluster_id "1"
160 | xcoord "419"
161 | parent_node "initial"
162 | ]
163 | node [
164 | id 16
165 | label "assemblage-97-446"
166 | appears_in_slice "1"
167 | ycoord "446"
168 | level "None"
169 | cluster_id "2"
170 | xcoord "97"
171 | parent_node "initial"
172 | ]
173 | node [
174 | id 17
175 | label "assemblage-74-450"
176 | appears_in_slice "1"
177 | ycoord "450"
178 | level "None"
179 | cluster_id "2"
180 | xcoord "74"
181 | parent_node "initial"
182 | ]
183 | node [
184 | id 18
185 | label "assemblage-87-458"
186 | appears_in_slice "1"
187 | ycoord "458"
188 | level "None"
189 | cluster_id "2"
190 | xcoord "87"
191 | parent_node "initial"
192 | ]
193 | node [
194 | id 19
195 | label "assemblage-82-436"
196 | appears_in_slice "1"
197 | ycoord "436"
198 | level "None"
199 | cluster_id "2"
200 | xcoord "82"
201 | parent_node "initial"
202 | ]
203 | node [
204 | id 20
205 | label "assemblage-92-441"
206 | appears_in_slice "1"
207 | ycoord "441"
208 | level "None"
209 | cluster_id "2"
210 | xcoord "92"
211 | parent_node "initial"
212 | ]
213 | node [
214 | id 21
215 | label "assemblage-108-430"
216 | appears_in_slice "1"
217 | ycoord "430"
218 | level "None"
219 | cluster_id "2"
220 | xcoord "108"
221 | parent_node "initial"
222 | ]
223 | node [
224 | id 22
225 | label "assemblage-89-472"
226 | appears_in_slice "1"
227 | ycoord "472"
228 | level "None"
229 | cluster_id "2"
230 | xcoord "89"
231 | parent_node "initial"
232 | ]
233 | node [
234 | id 23
235 | label "assemblage-87-456"
236 | appears_in_slice "1"
237 | ycoord "456"
238 | level "None"
239 | cluster_id "2"
240 | xcoord "87"
241 | parent_node "initial"
242 | ]
243 | edge [
244 | source 0
245 | target 1
246 | distance 4.12310562562
247 | normalized_weight 0.5
248 | unnormalized_weight 0.5
249 | weight 0.5
250 | ]
251 | edge [
252 | source 0
253 | target 2
254 | distance 2.0
255 | normalized_weight 0.5
256 | unnormalized_weight 0.5
257 | weight 0.5
258 | ]
259 | edge [
260 | source 0
261 | target 3
262 | distance 5.56776436283
263 | normalized_weight 0.5
264 | unnormalized_weight 0.5
265 | weight 0.5
266 | ]
267 | edge [
268 | source 0
269 | target 4
270 | distance 4.69041575982
271 | normalized_weight 0.5
272 | unnormalized_weight 0.5
273 | weight 0.5
274 | ]
275 | edge [
276 | source 0
277 | target 5
278 | distance 2.82842712475
279 | normalized_weight 0.5
280 | unnormalized_weight 0.5
281 | weight 0.5
282 | ]
283 | edge [
284 | source 0
285 | target 6
286 | distance 3.0
287 | normalized_weight 0.5
288 | unnormalized_weight 0.5
289 | weight 0.5
290 | ]
291 | edge [
292 | source 0
293 | target 7
294 | distance 5.9160797831
295 | normalized_weight 0.5
296 | unnormalized_weight 0.5
297 | weight 0.5
298 | ]
299 | edge [
300 | source 0
301 | target 9
302 | distance 18.0277563773
303 | ]
304 | edge [
305 | source 1
306 | target 2
307 | distance 4.58257569496
308 | normalized_weight 0.5
309 | unnormalized_weight 0.5
310 | weight 0.5
311 | ]
312 | edge [
313 | source 1
314 | target 3
315 | distance 4.69041575982
316 | normalized_weight 0.5
317 | unnormalized_weight 0.5
318 | weight 0.5
319 | ]
320 | edge [
321 | source 1
322 | target 4
323 | distance 2.64575131106
324 | normalized_weight 0.5
325 | unnormalized_weight 0.5
326 | weight 0.5
327 | ]
328 | edge [
329 | source 1
330 | target 5
331 | distance 3.87298334621
332 | normalized_weight 0.5
333 | unnormalized_weight 0.5
334 | weight 0.5
335 | ]
336 | edge [
337 | source 1
338 | target 6
339 | distance 4.24264068712
340 | normalized_weight 0.5
341 | unnormalized_weight 0.5
342 | weight 0.5
343 | ]
344 | edge [
345 | source 1
346 | target 7
347 | distance 4.24264068712
348 | normalized_weight 0.5
349 | unnormalized_weight 0.5
350 | weight 0.5
351 | ]
352 | edge [
353 | source 1
354 | target 22
355 | distance 12.9228479833
356 | ]
357 | edge [
358 | source 2
359 | target 3
360 | distance 5.9160797831
361 | normalized_weight 0.5
362 | unnormalized_weight 0.5
363 | weight 0.5
364 | ]
365 | edge [
366 | source 2
367 | target 4
368 | distance 5.09901951359
369 | normalized_weight 0.5
370 | unnormalized_weight 0.5
371 | weight 0.5
372 | ]
373 | edge [
374 | source 2
375 | target 5
376 | distance 2.82842712475
377 | normalized_weight 0.5
378 | unnormalized_weight 0.5
379 | weight 0.5
380 | ]
381 | edge [
382 | source 2
383 | target 6
384 | distance 3.0
385 | normalized_weight 0.5
386 | unnormalized_weight 0.5
387 | weight 0.5
388 | ]
389 | edge [
390 | source 2
391 | target 7
392 | distance 6.2449979984
393 | normalized_weight 0.5
394 | unnormalized_weight 0.5
395 | weight 0.5
396 | ]
397 | edge [
398 | source 2
399 | target 15
400 | distance 18.1107702763
401 | ]
402 | edge [
403 | source 2
404 | target 16
405 | distance 10.5830052443
406 | ]
407 | edge [
408 | source 3
409 | target 4
410 | distance 5.38516480713
411 | normalized_weight 0.5
412 | unnormalized_weight 0.5
413 | weight 0.5
414 | ]
415 | edge [
416 | source 3
417 | target 5
418 | distance 5.38516480713
419 | normalized_weight 0.5
420 | unnormalized_weight 0.5
421 | weight 0.5
422 | ]
423 | edge [
424 | source 3
425 | target 6
426 | distance 5.65685424949
427 | normalized_weight 0.5
428 | unnormalized_weight 0.5
429 | weight 0.5
430 | ]
431 | edge [
432 | source 3
433 | target 7
434 | distance 4.69041575982
435 | normalized_weight 0.5
436 | unnormalized_weight 0.5
437 | weight 0.5
438 | ]
439 | edge [
440 | source 4
441 | target 5
442 | distance 4.472135955
443 | normalized_weight 0.5
444 | unnormalized_weight 0.5
445 | weight 0.5
446 | ]
447 | edge [
448 | source 4
449 | target 6
450 | distance 4.79583152331
451 | normalized_weight 0.5
452 | unnormalized_weight 0.5
453 | weight 0.5
454 | ]
455 | edge [
456 | source 4
457 | target 7
458 | distance 3.60555127546
459 | normalized_weight 0.5
460 | unnormalized_weight 0.5
461 | weight 0.5
462 | ]
463 | edge [
464 | source 5
465 | target 6
466 | distance 1.73205080757
467 | normalized_weight 0.5
468 | unnormalized_weight 0.5
469 | weight 0.5
470 | ]
471 | edge [
472 | source 5
473 | target 7
474 | distance 5.74456264654
475 | normalized_weight 0.5
476 | unnormalized_weight 0.5
477 | weight 0.5
478 | ]
479 | edge [
480 | source 5
481 | target 10
482 | distance 18.7616630393
483 | ]
484 | edge [
485 | source 6
486 | target 7
487 | distance 6.0
488 | normalized_weight 0.5
489 | unnormalized_weight 0.5
490 | weight 0.5
491 | ]
492 | edge [
493 | source 7
494 | target 17
495 | distance 13.3416640641
496 | ]
497 | edge [
498 | source 8
499 | target 9
500 | distance 4.0
501 | normalized_weight 0.5
502 | unnormalized_weight 0.5
503 | weight 0.5
504 | ]
505 | edge [
506 | source 8
507 | target 10
508 | distance 6.7082039325
509 | normalized_weight 0.5
510 | unnormalized_weight 0.5
511 | weight 0.5
512 | ]
513 | edge [
514 | source 8
515 | target 11
516 | distance 3.74165738677
517 | normalized_weight 0.5
518 | unnormalized_weight 0.5
519 | weight 0.5
520 | ]
521 | edge [
522 | source 8
523 | target 12
524 | distance 7.07106781187
525 | normalized_weight 0.5
526 | unnormalized_weight 0.5
527 | weight 0.5
528 | ]
529 | edge [
530 | source 8
531 | target 13
532 | distance 5.74456264654
533 | normalized_weight 0.5
534 | unnormalized_weight 0.5
535 | weight 0.5
536 | ]
537 | edge [
538 | source 8
539 | target 14
540 | distance 6.7082039325
541 | normalized_weight 0.5
542 | unnormalized_weight 0.5
543 | weight 0.5
544 | ]
545 | edge [
546 | source 8
547 | target 15
548 | distance 5.56776436283
549 | normalized_weight 0.5
550 | unnormalized_weight 0.5
551 | weight 0.5
552 | ]
553 | edge [
554 | source 9
555 | target 10
556 | distance 5.38516480713
557 | normalized_weight 0.5
558 | unnormalized_weight 0.5
559 | weight 0.5
560 | ]
561 | edge [
562 | source 9
563 | target 11
564 | distance 3.74165738677
565 | normalized_weight 0.5
566 | unnormalized_weight 0.5
567 | weight 0.5
568 | ]
569 | edge [
570 | source 9
571 | target 12
572 | distance 5.83095189485
573 | normalized_weight 0.5
574 | unnormalized_weight 0.5
575 | weight 0.5
576 | ]
577 | edge [
578 | source 9
579 | target 13
580 | distance 4.12310562562
581 | normalized_weight 0.5
582 | unnormalized_weight 0.5
583 | weight 0.5
584 | ]
585 | edge [
586 | source 9
587 | target 14
588 | distance 5.38516480713
589 | normalized_weight 0.5
590 | unnormalized_weight 0.5
591 | weight 0.5
592 | ]
593 | edge [
594 | source 9
595 | target 15
596 | distance 6.5574385243
597 | normalized_weight 0.5
598 | unnormalized_weight 0.5
599 | weight 0.5
600 | ]
601 | edge [
602 | source 10
603 | target 11
604 | distance 5.56776436283
605 | normalized_weight 0.5
606 | unnormalized_weight 0.5
607 | weight 0.5
608 | ]
609 | edge [
610 | source 10
611 | target 12
612 | distance 4.12310562562
613 | normalized_weight 0.5
614 | unnormalized_weight 0.5
615 | weight 0.5
616 | ]
617 | edge [
618 | source 10
619 | target 13
620 | distance 3.46410161514
621 | normalized_weight 0.5
622 | unnormalized_weight 0.5
623 | weight 0.5
624 | ]
625 | edge [
626 | source 10
627 | target 14
628 | distance 3.74165738677
629 | normalized_weight 0.5
630 | unnormalized_weight 0.5
631 | weight 0.5
632 | ]
633 | edge [
634 | source 10
635 | target 15
636 | distance 6.78232998313
637 | normalized_weight 0.5
638 | unnormalized_weight 0.5
639 | weight 0.5
640 | ]
641 | edge [
642 | source 10
643 | target 21
644 | distance 21.0475651798
645 | ]
646 | edge [
647 | source 11
648 | target 12
649 | distance 6.0
650 | normalized_weight 0.5
651 | unnormalized_weight 0.5
652 | weight 0.5
653 | ]
654 | edge [
655 | source 11
656 | target 13
657 | distance 5.0
658 | normalized_weight 0.5
659 | unnormalized_weight 0.5
660 | weight 0.5
661 | ]
662 | edge [
663 | source 11
664 | target 14
665 | distance 5.56776436283
666 | normalized_weight 0.5
667 | unnormalized_weight 0.5
668 | weight 0.5
669 | ]
670 | edge [
671 | source 11
672 | target 15
673 | distance 5.38516480713
674 | normalized_weight 0.5
675 | unnormalized_weight 0.5
676 | weight 0.5
677 | ]
678 | edge [
679 | source 12
680 | target 13
681 | distance 5.0
682 | normalized_weight 0.5
683 | unnormalized_weight 0.5
684 | weight 0.5
685 | ]
686 | edge [
687 | source 12
688 | target 14
689 | distance 2.2360679775
690 | normalized_weight 0.5
691 | unnormalized_weight 0.5
692 | weight 0.5
693 | ]
694 | edge [
695 | source 12
696 | target 15
697 | distance 5.9160797831
698 | normalized_weight 0.5
699 | unnormalized_weight 0.5
700 | weight 0.5
701 | ]
702 | edge [
703 | source 12
704 | target 17
705 | distance 22.4053565024
706 | ]
707 | edge [
708 | source 13
709 | target 14
710 | distance 4.69041575982
711 | normalized_weight 0.5
712 | unnormalized_weight 0.5
713 | weight 0.5
714 | ]
715 | edge [
716 | source 13
717 | target 15
718 | distance 7.34846922835
719 | normalized_weight 0.5
720 | unnormalized_weight 0.5
721 | weight 0.5
722 | ]
723 | edge [
724 | source 13
725 | target 19
726 | distance 21.5174347914
727 | ]
728 | edge [
729 | source 14
730 | target 15
731 | distance 5.65685424949
732 | normalized_weight 0.5
733 | unnormalized_weight 0.5
734 | weight 0.5
735 | ]
736 | edge [
737 | source 16
738 | target 17
739 | distance 5.19615242271
740 | normalized_weight 0.5
741 | weight 0.5
742 | unnormalized_weight 0.5
743 | ]
744 | edge [
745 | source 16
746 | target 18
747 | distance 4.69041575982
748 | normalized_weight 0.5
749 | weight 0.5
750 | unnormalized_weight 0.5
751 | ]
752 | edge [
753 | source 16
754 | target 19
755 | distance 5.0
756 | normalized_weight 0.5
757 | weight 0.5
758 | unnormalized_weight 0.5
759 | ]
760 | edge [
761 | source 16
762 | target 20
763 | distance 3.16227766017
764 | normalized_weight 0.5
765 | weight 0.5
766 | unnormalized_weight 0.5
767 | ]
768 | edge [
769 | source 16
770 | target 21
771 | distance 5.19615242271
772 | normalized_weight 0.5
773 | weight 0.5
774 | unnormalized_weight 0.5
775 | ]
776 | edge [
777 | source 16
778 | target 22
779 | distance 5.83095189485
780 | normalized_weight 0.5
781 | weight 0.5
782 | unnormalized_weight 0.5
783 | ]
784 | edge [
785 | source 16
786 | target 23
787 | distance 4.472135955
788 | normalized_weight 0.5
789 | weight 0.5
790 | unnormalized_weight 0.5
791 | ]
792 | edge [
793 | source 17
794 | target 18
795 | distance 4.58257569496
796 | normalized_weight 0.5
797 | weight 0.5
798 | unnormalized_weight 0.5
799 | ]
800 | edge [
801 | source 17
802 | target 19
803 | distance 4.69041575982
804 | normalized_weight 0.5
805 | weight 0.5
806 | unnormalized_weight 0.5
807 | ]
808 | edge [
809 | source 17
810 | target 20
811 | distance 5.19615242271
812 | normalized_weight 0.5
813 | weight 0.5
814 | unnormalized_weight 0.5
815 | ]
816 | edge [
817 | source 17
818 | target 21
819 | distance 7.34846922835
820 | normalized_weight 0.5
821 | weight 0.5
822 | unnormalized_weight 0.5
823 | ]
824 | edge [
825 | source 17
826 | target 22
827 | distance 6.0827625303
828 | normalized_weight 0.5
829 | weight 0.5
830 | unnormalized_weight 0.5
831 | ]
832 | edge [
833 | source 17
834 | target 23
835 | distance 4.35889894354
836 | normalized_weight 0.5
837 | weight 0.5
838 | unnormalized_weight 0.5
839 | ]
840 | edge [
841 | source 18
842 | target 19
843 | distance 5.19615242271
844 | normalized_weight 0.5
845 | weight 0.5
846 | unnormalized_weight 0.5
847 | ]
848 | edge [
849 | source 18
850 | target 20
851 | distance 4.69041575982
852 | normalized_weight 0.5
853 | weight 0.5
854 | unnormalized_weight 0.5
855 | ]
856 | edge [
857 | source 18
858 | target 21
859 | distance 7.0
860 | normalized_weight 0.5
861 | weight 0.5
862 | unnormalized_weight 0.5
863 | ]
864 | edge [
865 | source 18
866 | target 22
867 | distance 4.0
868 | normalized_weight 0.5
869 | weight 0.5
870 | unnormalized_weight 0.5
871 | ]
872 | edge [
873 | source 18
874 | target 23
875 | distance 1.41421356237
876 | normalized_weight 0.5
877 | weight 0.5
878 | unnormalized_weight 0.5
879 | ]
880 | edge [
881 | source 19
882 | target 20
883 | distance 3.87298334621
884 | normalized_weight 0.5
885 | weight 0.5
886 | unnormalized_weight 0.5
887 | ]
888 | edge [
889 | source 19
890 | target 21
891 | distance 5.65685424949
892 | normalized_weight 0.5
893 | weight 0.5
894 | unnormalized_weight 0.5
895 | ]
896 | edge [
897 | source 19
898 | target 22
899 | distance 6.5574385243
900 | normalized_weight 0.5
901 | weight 0.5
902 | unnormalized_weight 0.5
903 | ]
904 | edge [
905 | source 19
906 | target 23
907 | distance 5.0
908 | normalized_weight 0.5
909 | weight 0.5
910 | unnormalized_weight 0.5
911 | ]
912 | edge [
913 | source 20
914 | target 21
915 | distance 5.19615242271
916 | normalized_weight 0.5
917 | weight 0.5
918 | unnormalized_weight 0.5
919 | ]
920 | edge [
921 | source 20
922 | target 22
923 | distance 5.83095189485
924 | normalized_weight 0.5
925 | weight 0.5
926 | unnormalized_weight 0.5
927 | ]
928 | edge [
929 | source 20
930 | target 23
931 | distance 4.472135955
932 | normalized_weight 0.5
933 | weight 0.5
934 | unnormalized_weight 0.5
935 | ]
936 | edge [
937 | source 21
938 | target 22
939 | distance 7.81024967591
940 | normalized_weight 0.5
941 | weight 0.5
942 | unnormalized_weight 0.5
943 | ]
944 | edge [
945 | source 21
946 | target 23
947 | distance 6.8556546004
948 | normalized_weight 0.5
949 | weight 0.5
950 | unnormalized_weight 0.5
951 | ]
952 | edge [
953 | source 22
954 | target 23
955 | distance 4.24264068712
956 | normalized_weight 0.5
957 | weight 0.5
958 | unnormalized_weight 0.5
959 | ]
960 | ]
961 |
--------------------------------------------------------------------------------