| 168 | | |
| 169 | | N = projection.post.size |
| 170 | | rarr = projection.rng.next(N, 'uniform', (0,1), mask_local=False) |
| 171 | | if not core.is_listlike(rarr) and numpy.isscalar(rarr): # if N=1, rarr will be a single number |
| 172 | | rarr = numpy.array([rarr]) |
| 173 | | global_target_mask = rarr < p |
| 174 | | local_target_mask = global_target_mask[local] |
| 175 | | targets = projection.post.local_cells[local_target_mask].tolist() |
| 176 | | |
| 177 | | if len(targets) > 0: |
| 178 | | distance_matrix = DistanceMatrix(src.position, |
| 179 | | projection.post.positions, |
| 180 | | space=self.space) |
| 181 | | weights = weight_generator.get(global_target_mask, distance_matrix) |
| 182 | | delays = delay_generator.get(global_target_mask, distance_matrix) |
| 183 | | projection.connection_manager.connect(src, targets, weights, delays) |
| 184 | | |
| | 267 | connector._probabilistic_connect(src, self.p_connect) |
| | 268 | |
| | 269 | |
| | 270 | class DistanceDependentProbabilityConnector(ProbabilisticConnector): |
| | 271 | """ |
| | 272 | For each pair of pre-post cells, the connection probability depends on distance. |
| | 273 | """ |
| | 274 | |
| | 275 | def __init__(self, d_expression, allow_self_connections=True, |
| | 276 | weights=0.0, delays=None, space=Space(), safe=True): |
| | 277 | """ |
| | 278 | Create a new connector. |
| | 279 | , projection |
| | 280 | `d_expression` -- the right-hand side of a valid python expression for |
| | 281 | probability, involving 'd', e.g. "exp(-abs(d))", or "d<3" |
| | 282 | `space` -- a Space object. |
| | 283 | `weights` -- may either be a float, a RandomDistribution object, a list/ |
| | 284 | 1D array with at least as many items as connections to be |
| | 285 | created, or a DistanceDependence object. Units nA. |
| | 286 | `delays` -- as `weights`. If `None`, all synaptic delays will be set |
| | 287 | to the global minimum delay. |
| | 288 | """ |
| | 289 | Connector.__init__(self, weights, delays, space, safe) |
| | 290 | assert isinstance(d_expression, str) |
| | 291 | try: |
| | 292 | d = 0; assert 0 <= eval(d_expression), eval(d_expression) |
| | 293 | d = 1e12; assert 0 <= eval(d_expression), eval(d_expression) |
| | 294 | except ZeroDivisionError, err: |
| | 295 | raise ZeroDivisionError("Error in the distance expression %s. %s" % (d_expression, err)) |
| | 296 | self.d_expression = d_expression |
| | 297 | assert isinstance(allow_self_connections, bool) |
| | 298 | self.allow_self_connections = allow_self_connections |
| | 299 | |
| | 300 | def connect(self, projection): |
| | 301 | """Connect-up a Projection.""" |
| | 302 | connector = ProbabilisticConnector(projection, self.weights, self.delays, self.allow_self_connections, self.space, safe=self.safe) |
| | 303 | proba_generator = ProbaGenerator(self.d_expression, connector.local) |
| | 304 | |
| | 305 | for src in projection.pre.all(): |
| | 306 | connector.distance_matrix.set_source(src.position) |
| | 307 | proba = proba_generator.get(connector.N, connector.distance_matrix) |
| | 308 | if proba.dtype == 'bool': |
| | 309 | proba = proba.astype(float) |
| | 310 | connector._probabilistic_connect(src, proba) |
| | 311 | |
| | 312 | |
| | 313 | class FromListConnector(Connector): |
| | 314 | """ |
| | 315 | Make connections according to a list. |
| | 316 | """ |
| | 317 | |
| | 318 | def __init__(self, conn_list): |
| | 319 | """ |
| | 320 | Create a new connector. |
| | 321 | |
| | 322 | `conn_list` -- a list of tuples, one tuple for each connection. Each |
| | 323 | tuple should contain: |
| | 324 | (pre_addr, post_addr, weight, delay) |
| | 325 | where pre_addr is the address (a tuple) of the presynaptic |
| | 326 | neuron, and post_addr is the address of the postsynaptic |
| | 327 | neuron. |
| | 328 | """ |
| | 329 | # needs extending for dynamic synapses. |
| | 330 | Connector.__init__(self, 0., common.get_min_delay()) |
| | 331 | self.conn_list = conn_list |
| | 332 | |
| | 333 | def connect(self, projection): |
| | 334 | """Connect-up a Projection.""" |
| | 335 | # slow: should maybe sort by pre |
| | 336 | for i in xrange(len(self.conn_list)): |
| | 337 | src, tgt, weight, delay = self.conn_list[i][:] |
| | 338 | src = projection.pre[tuple(src)] |
| | 339 | tgt = projection.post[tuple(tgt)] |
| | 340 | projection.connection_manager.connect(src, [tgt], weight, delay) |
| | 341 | |
| | 342 | |
| | 343 | class FromFileConnector(FromListConnector): |
| | 344 | """ |
| | 345 | Make connections according to a list read from a file. |
| | 346 | """ |
| | 347 | |
| | 348 | def __init__(self, filename, distributed=False): |
| | 349 | """ |
| | 350 | Create a new connector. |
| | 351 | |
| | 352 | `filename` -- name of a text file containing a list of connections, in |
| | 353 | the format required by `FromListConnector`. |
| | 354 | `distributed` -- if this is True, then each node will read connections |
| | 355 | from a file called `filename.x`, where `x` is the MPI |
| | 356 | rank. This speeds up loading connections for |
| | 357 | distributed simulations. |
| | 358 | """ |
| | 359 | Connector.__init__(self, 0., common.get_min_delay()) |
| | 360 | self.filename = filename |
| | 361 | self.distributed = distributed |
| | 362 | |
| | 363 | def connect(self, projection): |
| | 364 | """Connect-up a Projection.""" |
| | 365 | if self.distributed: |
| | 366 | self.filename += ".%d" % common.rank() |
| | 367 | # open the file... |
| | 368 | f = open(self.filename, 'r', 10000) |
| | 369 | lines = f.readlines() |
| | 370 | f.close() |
| | 371 | # gather all the data in a list of tuples (one per line) |
| | 372 | input_tuples = [] |
| | 373 | for line in lines: |
| | 374 | single_line = line.rstrip() |
| | 375 | src, tgt, w, d = single_line.split("\t", 4) |
| | 376 | src = "[%s" % src.split("[",1)[1] |
| | 377 | tgt = "[%s" % tgt.split("[",1)[1] |
| | 378 | input_tuples.append((eval(src), eval(tgt), float(w), float(d))) |
| | 379 | self.conn_list = input_tuples |
| | 380 | FromListConnector.connect(self, projection) |
| | 381 | |
| | 382 | |
| | 383 | |
| | 384 | class FixedNumberPostConnector(Connector): |
| | 385 | """ |
| | 386 | Each pre-synaptic neuron is connected to exactly n post-synaptic neurons |
| | 387 | chosen at random. |
| | 388 | |
| | 389 | If n is less than the size of the post-synaptic population, there are no |
| | 390 | multiple connections, i.e., no instances of the same pair of neurons being |
| | 391 | multiply connected. If n is greater than the size of the post-synaptic |
| | 392 | population, all possible single connections are made before starting to add |
| | 393 | duplicate connections. |
| | 394 | """ |
| | 395 | |
| | 396 | def __init__(self, n, allow_self_connections=True, weights=0.0, delays=None, space=Space(), safe=True): |
| | 397 | """ |
| | 398 | Create a new connector. |
| | 399 | |
| | 400 | `n` -- either a positive integer, or a `RandomDistribution` that produces |
| | 401 | positive integers. If `n` is a `RandomDistribution`, then the |
| | 402 | number of post-synaptic neurons is drawn from this distribution |
| | 403 | for each pre-synaptic neuron. |
| | 404 | `allow_self_connections` -- if the connector is used to connect a |
| | 405 | Population to itself, this flag determines whether a neuron is |
| | 406 | allowed to connect to itself, or only to other neurons in the |
| | 407 | Population. |
| | 408 | `weights` -- may either be a float, a RandomDistribution object, a list/ |
| | 409 | 1D array with at least as many items as connections to be |
| | 410 | created. Units nA. |
| | 411 | `delays` -- as `weights`. If `None`, all synaptic delays will be set |
| | 412 | to the global minimum delay. |
| | 413 | """ |
| | 414 | Connector.__init__(self, weights, delays, space, safe) |
| | 415 | assert isinstance(allow_self_connections, bool) |
| | 416 | self.allow_self_connections = allow_self_connections |
| | 417 | if isinstance(n, int): |
| | 418 | self.n = n |
| | 419 | assert n >= 0 |
| | 420 | elif isinstance(n, random.RandomDistribution): |
| | 421 | self.rand_distr = n |
| | 422 | # weak check that the random distribution is ok |
| | 423 | assert numpy.all(numpy.array(n.next(100)) >= 0), "the random distribution produces negative numbers" |
| | 424 | else: |
| | 425 | raise Exception("n must be an integer or a RandomDistribution object") |
| | 426 | |
| | 427 | def connect(self, projection): |
| | 428 | """Connect-up a Projection.""" |
| | 429 | local = projection.post._mask_local.flatten() |
| | 430 | weights_generator = WeightGenerator(self.weights, local, projection, self.safe) |
| | 431 | delays_generator = DelayGenerator(self.delays, local, self.safe) |
| | 432 | distance_matrix = DistanceMatrix(projection.post.positions, self.space, local) |
| | 433 | candidates = projection.post.all_cells.flatten() |
| | 434 | |
| | 435 | if isinstance(projection.rng, random.NativeRNG): |
| | 436 | raise Exception("Use of NativeRNG not implemented.") |
| | 437 | |
| | 438 | for src in projection.pre.all(): |
| | 439 | # pick n neurons at random |
| | 440 | if hasattr(self, 'rand_distr'): |
| | 441 | n = self.rand_distr.next() |
| | 442 | else: |
| | 443 | n = self.n |
| | 444 | |
| | 445 | targets = numpy.zeros(0, int) |
| | 446 | loc_targets = [] |
| | 447 | create = [] |
| | 448 | |
| | 449 | while len(targets) < n: # if the number of requested cells is larger than the size of the |
| | 450 | # postsynaptic population, we allow multiple connections for a given cell |
| | 451 | ids = projection.rng.permutation(candidates)[0:n] - projection.post.first_id |
| | 452 | targets = numpy.concatenate((targets, candidates[ids.astype(int)])) |
| | 453 | if not self.allow_self_connections and projection.pre == projection.post: |
| | 454 | idx = numpy.where(targets == src)[0] |
| | 455 | targets = numpy.delete(targets, idx) |
| | 456 | |
| | 457 | targets = targets[:n] |
| | 458 | if isinstance(self.weights, basestring) or isinstance(self.delays, basestring): |
| | 459 | distance_matrix.set_source(src.position) |
| | 460 | |
| | 461 | # We need to keep only the local cells, because then distances will be computed only |
| | 462 | # by the nodes that will established the connections. create is just a mask with all |
| | 463 | # the id of the cells that are local and that need to be used. Same id could be repeted |
| | 464 | # but this is not a problem. |
| | 465 | for id in targets: |
| | 466 | pos = numpy.where(id == projection.post.local_cells)[0] |
| | 467 | N = len(pos) |
| | 468 | if N > 0: |
| | 469 | loc_targets += N*[id] |
| | 470 | create += [pos[0]] |
| | 471 | |
| | 472 | weights = weights_generator.get(n, distance_matrix, create) |
| | 473 | delays = delays_generator.get(n, distance_matrix, create) |
| | 474 | |
| | 475 | if len(loc_targets) > 0: |
| | 476 | projection.connection_manager.connect(src, loc_targets, weights, delays) |
| | 477 | |
| | 478 | |
| | 479 | class FixedNumberPreConnector(Connector): |
| | 480 | """ |
| | 481 | Each post-synaptic neuron is connected to exactly n pre-synaptic neurons |
| | 482 | chosen at random. |
| | 483 | |
| | 484 | If n is less than the size of the pre-synaptic population, there are no |
| | 485 | multiple connections, i.e., no instances of the same pair of neurons being |
| | 486 | multiply connected. If n is greater than the size of the pre-synaptic |
| | 487 | population, all possible single connections are made before starting to add |
| | 488 | duplicate connections. |
| | 489 | """ |
| | 490 | |
| | 491 | def __init__(self, n, allow_self_connections=True, weights=0.0, delays=None, space=Space(), safe=True): |
| | 492 | """ |
| | 493 | Create a new connector. |
| | 494 | |
| | 495 | `n` -- either a positive integer, or a `RandomDistribution` that produces |
| | 496 | positive integers. If `n` is a `RandomDistribution`, then the |
| | 497 | number of pre-synaptic neurons is drawn from this distribution |
| | 498 | for each post-synaptic neuron. |
| | 499 | `allow_self_connections` -- if the connector is used to connect a |
| | 500 | Population to itself, this flag determines whether a neuron is |
| | 501 | allowed to connect to itself, or only to other neurons in the |
| | 502 | Population. |
| | 503 | `weights` -- may either be a float, a RandomDistribution object, a list/ |
| | 504 | 1D array with at least as many items as connections to be |
| | 505 | created. Units nA. |
| | 506 | `delays` -- as `weights`. If `None`, all synaptic delays will be set |
| | 507 | to the global minimum delay. |
| | 508 | """ |
| | 509 | Connector.__init__(self, weights, delays, space, safe) |
| | 510 | assert isinstance(allow_self_connections, bool) |
| | 511 | self.allow_self_connections = allow_self_connections |
| | 512 | if isinstance(n, int): |
| | 513 | self.n = n |
| | 514 | assert n >= 0 |
| | 515 | elif isinstance(n, random.RandomDistribution): |
| | 516 | self.rand_distr = n |
| | 517 | # weak check that the random distribution is ok |
| | 518 | assert numpy.all(numpy.array(n.next(100)) >= 0), "the random distribution produces negative numbers" |
| | 519 | else: |
| | 520 | raise Exception("n must be an integer or a RandomDistribution object") |
| | 521 | |
| | 522 | def connect(self, projection): |
| | 523 | """Connect-up a Projection.""" |
| | 524 | local = numpy.arange(len(projection.post)) |
| | 525 | weights_generator = WeightGenerator(self.weights, local, projection, self.safe) |
| | 526 | delays_generator = DelayGenerator(self.delays, local, self.safe) |
| | 527 | distance_matrix = DistanceMatrix(projection.pre.positions, self.space) |
| | 528 | candidates = projection.pre.all_cells.flatten() |
| | 529 | |
| | 530 | if isinstance(projection.rng, random.NativeRNG): |
| | 531 | raise Exception("Warning: use of NativeRNG not implemented.") |
| | 532 | |
| | 533 | for tgt in projection.post.local_cells.flat: |
| | 534 | # pick n neurons at random |
| | 535 | if hasattr(self, 'rand_distr'): |
| | 536 | n = self.rand_distr.next() |
| | 537 | else: |
| | 538 | n = self.n |
| | 539 | |
| | 540 | sources = [] |
| | 541 | while len(sources) < n: # if the number of requested cells is larger than the size of the |
| | 542 | # presynaptic population, we allow multiple connections for a given cell |
| | 543 | ids = projection.rng.permutation(candidates)[0:n] - projection.pre.first_id |
| | 544 | sources = numpy.concatenate((sources, candidates[ids.astype(int)])) |
| | 545 | if not self.allow_self_connections and projection.pre == projection.post: |
| | 546 | i = numpy.where(sources == tgt)[0] |
| | 547 | sources = numpy.delete(sources, i) |
| | 548 | |
| | 549 | sources = sources[:n].astype(int) |
| | 550 | |
| | 551 | if isinstance(self.weights, basestring) or isinstance(self.delays, basestring): |
| | 552 | distance_matrix.set_source(tgt.position) |
| | 553 | |
| | 554 | create = sources - projection.pre.first_id |
| | 555 | weights = weights_generator.get(n, distance_matrix, create) |
| | 556 | delays = delays_generator.get(n, distance_matrix, create) |
| | 557 | |
| | 558 | for src, w, d in zip(sources, weights, delays): |
| | 559 | projection.connection_manager.connect(src, [tgt], w, d) |
| | 560 | |
| | 561 | |
| | 562 | class OneToOneConnector(Connector): |
| | 563 | """ |
| | 564 | Where the pre- and postsynaptic populations have the same size, connect |
| | 565 | cell i in the presynaptic population to cell i in the postsynaptic |
| | 566 | population for all i. |
| | 567 | """ |
| | 568 | #In fact, despite the name, this should probably be generalised to the |
| | 569 | #case where the pre and post populations have different dimensions, e.g., |
| | 570 | #cell i in a 1D pre population of size n should connect to all cells |
| | 571 | #in row i of a 2D post population of size (n,m). |
| | 572 | |
| | 573 | |
| | 574 | def __init__(self, weights=0.0, delays=None, space=Space()): |
| | 575 | """ |
| | 576 | Create a new connector. |
| | 577 | |
| | 578 | `weights` -- may either be a float, a RandomDistribution object, a list/ |
| | 579 | 1D array with at least as many items as connections to be |
| | 580 | created. Units nA. |
| | 581 | `delays` -- as `weights`. If `None`, all synaptic delays will be set |
| | 582 | to the global minimum delay. |
| | 583 | """ |
| | 584 | Connector.__init__(self, weights, delays, space) |
| | 585 | |
| | 586 | def connect(self, projection): |
| | 587 | """Connect-up a Projection.""" |
| | 588 | if projection.pre.dim == projection.post.dim: |
| | 589 | N = projection.post.size |
| | 590 | local = projection.post._mask_local.flatten() |
| | 591 | weights_generator = WeightGenerator(self.weights, local, projection) |
| | 592 | delays_generator = DelayGenerator(self.delays, local) |
| | 593 | weights = weights_generator.get(N) |
| | 594 | delays = delays_generator.get(N) |
| | 595 | |
| | 596 | for tgt, w, d in zip(projection.post.local_cells, weights, delays): |
| | 597 | src = projection.pre.index(projection.post.id_to_index(tgt)) |
| | 598 | |
| | 599 | # the float is in case the values are of type numpy.float64, which NEST chokes on |
| | 600 | projection.connection_manager.connect(src, [tgt], float(w), float(d)) |
| | 601 | else: |
| | 602 | raise errors.InvalidDimensionsError("OneToOneConnector does not support presynaptic and postsynaptic Populations of different sizes.") |