├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── MANIFEST.in ├── Makefile.am ├── NEWS ├── README.md ├── RELEASE ├── TODO ├── acinclude.m4 ├── aclocal.sh ├── autogen.sh ├── configure.ac ├── continuous_integration ├── install.sh └── test_script.sh ├── csa ├── ChangeLog ├── __init__.py.in ├── _elementary.py ├── _misc.py ├── closure.py ├── conngen.py ├── connset.py ├── csaobject.py ├── elementary.py ├── geometry.py ├── intervalset.py ├── misc.py ├── plot.py ├── valueset.py └── version.py ├── debian ├── changelog ├── compat ├── control ├── copyright ├── python-csa.docs ├── rules └── source │ └── format ├── doc ├── ChangeLog ├── figures │ ├── blockRandom.pdf │ ├── brro.pdf │ ├── cartesian.pdf │ ├── disc.pdf │ ├── gaussnet.pdf │ ├── grid2d.pdf │ ├── intersection.pdf │ ├── oneToOne.pdf │ ├── projection.pdf │ ├── random.pdf │ ├── random2d.pdf │ ├── setDifference.pdf │ ├── twoPoints.pdf │ └── visualDisc.pdf ├── manual │ ├── basicfunc.rst │ ├── datatypes.rst │ ├── geometry.rst │ ├── index.rst │ ├── intro.rst │ ├── random.rst │ └── tutorial.rst └── tutorial.tex ├── examples └── catLGNV1.py ├── libpycsa ├── Makefile.am ├── pycsa.cpp └── pycsa.h ├── setup.py └── tests └── test_csa.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | aclocal.m4 3 | autom4te.cache 4 | compile 5 | config.guess 6 | config.h.in 7 | config.sub 8 | configure 9 | depcomp 10 | dist 11 | fontList.cache 12 | install-sh 13 | ltmain.sh 14 | Makefile.in 15 | MANIFEST 16 | missing 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.4" 5 | - "3.5" 6 | - "3.6" 7 | addons: 8 | apt: 9 | packages: 10 | install: 11 | - pip install coveralls matplotlib -q 12 | - source continuous_integration/install.sh 13 | script: 14 | - bash continuous_integration/test_script.sh 15 | after_success: 16 | coveralls 17 | cache: 18 | - apt 19 | - pip -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | To find out what should go in this file, see "Information For 2 | Maintainers of GNU Software" (maintain.texi), the section called 3 | "Recording Changes". 4 | 5 | Mikael Djurfeldt 6 | Espen Hagen 7 | Patrick Herbers 8 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2020-01-22 Mikael Djurfeldt 2 | 3 | * Version 0.1.10 4 | 5 | 2014-02-23 Mikael Djurfeldt 6 | 7 | * Version 0.1.6 8 | 9 | 2012-03-30 Mikael Djurfeldt 10 | 11 | * Release 0.1.0 12 | 13 | 2010-06-24 Mikael Djurfeldt 14 | 15 | * README: Updated "Getting Started" section. 16 | 17 | 2010-06-21 Mikael Djurfeldt 18 | 19 | * README: Added a "Getting Started" section. 20 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, 5 | Inc. 6 | 7 | Copying and distribution of this file, with or without modification, 8 | are permitted in any medium without royalty provided the copyright 9 | notice and this notice are preserved. This file is offered as-is, 10 | without warranty of any kind. 11 | 12 | Basic Installation 13 | ================== 14 | 15 | Briefly, the shell command `./configure && make && make install' 16 | should configure, build, and install this package. The following 17 | more-detailed instructions are generic; see the `README' file for 18 | instructions specific to this package. Some packages provide this 19 | `INSTALL' file but do not implement all of the features documented 20 | below. The lack of an optional feature in a given package is not 21 | necessarily a bug. More recommendations for GNU packages can be found 22 | in *note Makefile Conventions: (standards)Makefile Conventions. 23 | 24 | The `configure' shell script attempts to guess correct values for 25 | various system-dependent variables used during compilation. It uses 26 | those values to create a `Makefile' in each directory of the package. 27 | It may also create one or more `.h' files containing system-dependent 28 | definitions. Finally, it creates a shell script `config.status' that 29 | you can run in the future to recreate the current configuration, and a 30 | file `config.log' containing compiler output (useful mainly for 31 | debugging `configure'). 32 | 33 | It can also use an optional file (typically called `config.cache' 34 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 35 | the results of its tests to speed up reconfiguring. Caching is 36 | disabled by default to prevent problems with accidental use of stale 37 | cache files. 38 | 39 | If you need to do unusual things to compile the package, please try 40 | to figure out how `configure' could check whether to do them, and mail 41 | diffs or instructions to the address given in the `README' so they can 42 | be considered for the next release. If you are using the cache, and at 43 | some point `config.cache' contains results you don't want to keep, you 44 | may remove or edit it. 45 | 46 | The file `configure.ac' (or `configure.in') is used to create 47 | `configure' by a program called `autoconf'. You need `configure.ac' if 48 | you want to change it or regenerate `configure' using a newer version 49 | of `autoconf'. 50 | 51 | The simplest way to compile this package is: 52 | 53 | 1. `cd' to the directory containing the package's source code and type 54 | `./configure' to configure the package for your system. 55 | 56 | Running `configure' might take a while. While running, it prints 57 | some messages telling which features it is checking for. 58 | 59 | 2. Type `make' to compile the package. 60 | 61 | 3. Optionally, type `make check' to run any self-tests that come with 62 | the package, generally using the just-built uninstalled binaries. 63 | 64 | 4. Type `make install' to install the programs and any data files and 65 | documentation. When installing into a prefix owned by root, it is 66 | recommended that the package be configured and built as a regular 67 | user, and only the `make install' phase executed with root 68 | privileges. 69 | 70 | 5. Optionally, type `make installcheck' to repeat any self-tests, but 71 | this time using the binaries in their final installed location. 72 | This target does not install anything. Running this target as a 73 | regular user, particularly if the prior `make install' required 74 | root privileges, verifies that the installation completed 75 | correctly. 76 | 77 | 6. You can remove the program binaries and object files from the 78 | source code directory by typing `make clean'. To also remove the 79 | files that `configure' created (so you can compile the package for 80 | a different kind of computer), type `make distclean'. There is 81 | also a `make maintainer-clean' target, but that is intended mainly 82 | for the package's developers. If you use it, you may have to get 83 | all sorts of other programs in order to regenerate files that came 84 | with the distribution. 85 | 86 | 7. Often, you can also type `make uninstall' to remove the installed 87 | files again. In practice, not all packages have tested that 88 | uninstallation works correctly, even though it is required by the 89 | GNU Coding Standards. 90 | 91 | 8. Some packages, particularly those that use Automake, provide `make 92 | distcheck', which can by used by developers to test that all other 93 | targets like `make install' and `make uninstall' work correctly. 94 | This target is generally not run by end users. 95 | 96 | Compilers and Options 97 | ===================== 98 | 99 | Some systems require unusual options for compilation or linking that 100 | the `configure' script does not know about. Run `./configure --help' 101 | for details on some of the pertinent environment variables. 102 | 103 | You can give `configure' initial values for configuration parameters 104 | by setting variables in the command line or in the environment. Here 105 | is an example: 106 | 107 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 108 | 109 | *Note Defining Variables::, for more details. 110 | 111 | Compiling For Multiple Architectures 112 | ==================================== 113 | 114 | You can compile the package for more than one kind of computer at the 115 | same time, by placing the object files for each architecture in their 116 | own directory. To do this, you can use GNU `make'. `cd' to the 117 | directory where you want the object files and executables to go and run 118 | the `configure' script. `configure' automatically checks for the 119 | source code in the directory that `configure' is in and in `..'. This 120 | is known as a "VPATH" build. 121 | 122 | With a non-GNU `make', it is safer to compile the package for one 123 | architecture at a time in the source code directory. After you have 124 | installed the package for one architecture, use `make distclean' before 125 | reconfiguring for another architecture. 126 | 127 | On MacOS X 10.5 and later systems, you can create libraries and 128 | executables that work on multiple system types--known as "fat" or 129 | "universal" binaries--by specifying multiple `-arch' options to the 130 | compiler but only a single `-arch' option to the preprocessor. Like 131 | this: 132 | 133 | ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 134 | CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 135 | CPP="gcc -E" CXXCPP="g++ -E" 136 | 137 | This is not guaranteed to produce working output in all cases, you 138 | may have to build one architecture at a time and combine the results 139 | using the `lipo' tool if you have problems. 140 | 141 | Installation Names 142 | ================== 143 | 144 | By default, `make install' installs the package's commands under 145 | `/usr/local/bin', include files under `/usr/local/include', etc. You 146 | can specify an installation prefix other than `/usr/local' by giving 147 | `configure' the option `--prefix=PREFIX', where PREFIX must be an 148 | absolute file name. 149 | 150 | You can specify separate installation prefixes for 151 | architecture-specific files and architecture-independent files. If you 152 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 153 | PREFIX as the prefix for installing programs and libraries. 154 | Documentation and other data files still use the regular prefix. 155 | 156 | In addition, if you use an unusual directory layout you can give 157 | options like `--bindir=DIR' to specify different values for particular 158 | kinds of files. Run `configure --help' for a list of the directories 159 | you can set and what kinds of files go in them. In general, the 160 | default for these options is expressed in terms of `${prefix}', so that 161 | specifying just `--prefix' will affect all of the other directory 162 | specifications that were not explicitly provided. 163 | 164 | The most portable way to affect installation locations is to pass the 165 | correct locations to `configure'; however, many packages provide one or 166 | both of the following shortcuts of passing variable assignments to the 167 | `make install' command line to change installation locations without 168 | having to reconfigure or recompile. 169 | 170 | The first method involves providing an override variable for each 171 | affected directory. For example, `make install 172 | prefix=/alternate/directory' will choose an alternate location for all 173 | directory configuration variables that were expressed in terms of 174 | `${prefix}'. Any directories that were specified during `configure', 175 | but not in terms of `${prefix}', must each be overridden at install 176 | time for the entire installation to be relocated. The approach of 177 | makefile variable overrides for each directory variable is required by 178 | the GNU Coding Standards, and ideally causes no recompilation. 179 | However, some platforms have known limitations with the semantics of 180 | shared libraries that end up requiring recompilation when using this 181 | method, particularly noticeable in packages that use GNU Libtool. 182 | 183 | The second method involves providing the `DESTDIR' variable. For 184 | example, `make install DESTDIR=/alternate/directory' will prepend 185 | `/alternate/directory' before all installation names. The approach of 186 | `DESTDIR' overrides is not required by the GNU Coding Standards, and 187 | does not work on platforms that have drive letters. On the other hand, 188 | it does better at avoiding recompilation issues, and works well even 189 | when some directory options were not specified in terms of `${prefix}' 190 | at `configure' time. 191 | 192 | Optional Features 193 | ================= 194 | 195 | If the package supports it, you can cause programs to be installed 196 | with an extra prefix or suffix on their names by giving `configure' the 197 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 198 | 199 | Some packages pay attention to `--enable-FEATURE' options to 200 | `configure', where FEATURE indicates an optional part of the package. 201 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 202 | is something like `gnu-as' or `x' (for the X Window System). The 203 | `README' should mention any `--enable-' and `--with-' options that the 204 | package recognizes. 205 | 206 | For packages that use the X Window System, `configure' can usually 207 | find the X include and library files automatically, but if it doesn't, 208 | you can use the `configure' options `--x-includes=DIR' and 209 | `--x-libraries=DIR' to specify their locations. 210 | 211 | Some packages offer the ability to configure how verbose the 212 | execution of `make' will be. For these packages, running `./configure 213 | --enable-silent-rules' sets the default to minimal output, which can be 214 | overridden with `make V=1'; while running `./configure 215 | --disable-silent-rules' sets the default to verbose, which can be 216 | overridden with `make V=0'. 217 | 218 | Particular systems 219 | ================== 220 | 221 | On HP-UX, the default C compiler is not ANSI C compatible. If GNU 222 | CC is not installed, it is recommended to use the following options in 223 | order to use an ANSI C compiler: 224 | 225 | ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" 226 | 227 | and if that doesn't work, install pre-built binaries of GCC for HP-UX. 228 | 229 | HP-UX `make' updates targets which have the same time stamps as 230 | their prerequisites, which makes it generally unusable when shipped 231 | generated files such as `configure' are involved. Use GNU `make' 232 | instead. 233 | 234 | On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot 235 | parse its `' header file. The option `-nodtk' can be used as 236 | a workaround. If GNU CC is not installed, it is therefore recommended 237 | to try 238 | 239 | ./configure CC="cc" 240 | 241 | and if that doesn't work, try 242 | 243 | ./configure CC="cc -nodtk" 244 | 245 | On Solaris, don't put `/usr/ucb' early in your `PATH'. This 246 | directory contains several dysfunctional programs; working variants of 247 | these programs are available in `/usr/bin'. So, if you need `/usr/ucb' 248 | in your `PATH', put it _after_ `/usr/bin'. 249 | 250 | On Haiku, software installed for all users goes in `/boot/common', 251 | not `/usr/local'. It is recommended to use the following options: 252 | 253 | ./configure --prefix=/boot/common 254 | 255 | Specifying the System Type 256 | ========================== 257 | 258 | There may be some features `configure' cannot figure out 259 | automatically, but needs to determine by the type of machine the package 260 | will run on. Usually, assuming the package is built to be run on the 261 | _same_ architectures, `configure' can figure that out, but if it prints 262 | a message saying it cannot guess the machine type, give it the 263 | `--build=TYPE' option. TYPE can either be a short name for the system 264 | type, such as `sun4', or a canonical name which has the form: 265 | 266 | CPU-COMPANY-SYSTEM 267 | 268 | where SYSTEM can have one of these forms: 269 | 270 | OS 271 | KERNEL-OS 272 | 273 | See the file `config.sub' for the possible values of each field. If 274 | `config.sub' isn't included in this package, then this package doesn't 275 | need to know the machine type. 276 | 277 | If you are _building_ compiler tools for cross-compiling, you should 278 | use the option `--target=TYPE' to select the type of system they will 279 | produce code for. 280 | 281 | If you want to _use_ a cross compiler, that generates code for a 282 | platform different from the build platform, you should specify the 283 | "host" platform (i.e., that on which the generated programs will 284 | eventually be run) with `--host=TYPE'. 285 | 286 | Sharing Defaults 287 | ================ 288 | 289 | If you want to set default values for `configure' scripts to share, 290 | you can create a site shell script called `config.site' that gives 291 | default values for variables like `CC', `cache_file', and `prefix'. 292 | `configure' looks for `PREFIX/share/config.site' if it exists, then 293 | `PREFIX/etc/config.site' if it exists. Or, you can set the 294 | `CONFIG_SITE' environment variable to the location of the site script. 295 | A warning: not all `configure' scripts look for a site script. 296 | 297 | Defining Variables 298 | ================== 299 | 300 | Variables not defined in a site shell script can be set in the 301 | environment passed to `configure'. However, some packages may run 302 | configure again during the build, and the customized values of these 303 | variables may be lost. In order to avoid this problem, you should set 304 | them in the `configure' command line, using `VAR=value'. For example: 305 | 306 | ./configure CC=/usr/local2/bin/gcc 307 | 308 | causes the specified `gcc' to be used as the C compiler (unless it is 309 | overridden in the site shell script). 310 | 311 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 312 | an Autoconf limitation. Until the limitation is lifted, you can use 313 | this workaround: 314 | 315 | CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash 316 | 317 | `configure' Invocation 318 | ====================== 319 | 320 | `configure' recognizes the following options to control how it 321 | operates. 322 | 323 | `--help' 324 | `-h' 325 | Print a summary of all of the options to `configure', and exit. 326 | 327 | `--help=short' 328 | `--help=recursive' 329 | Print a summary of the options unique to this package's 330 | `configure', and exit. The `short' variant lists options used 331 | only in the top level, while the `recursive' variant lists options 332 | also present in any nested packages. 333 | 334 | `--version' 335 | `-V' 336 | Print the version of Autoconf used to generate the `configure' 337 | script, and exit. 338 | 339 | `--cache-file=FILE' 340 | Enable the cache: use and save the results of the tests in FILE, 341 | traditionally `config.cache'. FILE defaults to `/dev/null' to 342 | disable caching. 343 | 344 | `--config-cache' 345 | `-C' 346 | Alias for `--cache-file=config.cache'. 347 | 348 | `--quiet' 349 | `--silent' 350 | `-q' 351 | Do not print messages saying which checks are being made. To 352 | suppress all normal output, redirect it to `/dev/null' (any error 353 | messages will still be shown). 354 | 355 | `--srcdir=DIR' 356 | Look for the package's source code in directory DIR. Usually 357 | `configure' can determine that directory automatically. 358 | 359 | `--prefix=DIR' 360 | Use DIR as the installation prefix. *note Installation Names:: 361 | for more details, including other options available for fine-tuning 362 | the installation locations. 363 | 364 | `--no-create' 365 | `-n' 366 | Run the configure checks, but stop before creating any output 367 | files. 368 | 369 | `configure' also accepts some other, not widely useful, options. Run 370 | `configure --help' for more details. 371 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include setup.py MANIFEST.in 2 | include COPYING 3 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = @LIBPYCSA_SUBDIR@ 2 | 3 | PACKAGE_NAME = python-csa 4 | PACKAGE_VERSION = $(shell PYTHONPATH="@srcdir@/csa" python -c 'from version import __version__; print __version__') 5 | 6 | EXTRA_DIST = $(srcdir)/setup.py csa/*.py $(srcdir)/csa/*.py 7 | 8 | debdir = dist/csa-$(PACKAGE_VERSION) 9 | 10 | .PHONY: dist debian-source debian-package 11 | 12 | README: $(srcdir)/README.md 13 | ln -s $(srcdir)/README.md README 14 | 15 | dist/csa-$(PACKAGE_VERSION).tar.gz: 16 | $(PYTHON) setup.py sdist 17 | 18 | debian-source: dist/csa-$(PACKAGE_VERSION).tar.gz 19 | @test ! -e $(debdir) || ( echo "*** Remove directory dist/csa-${PACKAGE_VERSION}" && exit 1 ) 20 | cp -p dist/csa-$(PACKAGE_VERSION).tar.gz dist/$(PACKAGE_NAME)_$(PACKAGE_VERSION).orig.tar.gz 21 | ( cd dist; tar zxf $(PACKAGE_NAME)_$(PACKAGE_VERSION).orig.tar.gz ) 22 | mkdir $(debdir)/debian 23 | cp -pr debian $(debdir) 24 | 25 | debian-package: debian-source 26 | ( cd $(debdir) && dpkg-buildpackage '-mMikael Djurfeldt ' -rfakeroot && cd ../.. && rm -rf $(debdir) ) 27 | 28 | install-exec-hook: 29 | cd $(srcdir) &&\ 30 | ( test "$(srcdir)" != "$(builddir)" && cp "$(builddir)/csa/__init__.py" "$(srcdir)/csa"; true ) &&\ 31 | $(PYTHON) setup.py build --build-base=$(abs_builddir)/build install --prefix=$(DESTDIR)$(prefix) --install-lib=$(DESTDIR)$(pyexecdir) --install-scripts=$(DESTDIR)$(bindir) --install-data=$(DESTDIR)$(pkgdatadir) 32 | 33 | clean-local: 34 | -rm -rf $(srcdir)/csa/*.pyc $(abs_builddir)/build tex.cache 35 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | CSA NEWS --- history of user-visible changes. 2 | Copyright (C) 2012, 2018, 2020 Mikael Djurfeldt 3 | 4 | Please send CSA bug reports to mikael@djurfeldt.com. 5 | 6 | 7 | Changes in 0.1.12: 8 | 9 | * Bug fixes 10 | 11 | Adapt to change in iterator behavior (PEP-0479). 12 | 13 | 14 | Changes in 0.1.10: 15 | 16 | * Build Debian package for Python3 17 | 18 | * Bug fixes 19 | 20 | Further support for Python3. 21 | 22 | Properly specify requirements in setup tools. 23 | 24 | 25 | Changes in 0.1.8: 26 | 27 | * Support for Python3 28 | 29 | * Building in separate build directory enabled 30 | 31 | * New geometry functions grid3d and random3d 32 | 33 | * Bug fixes 34 | 35 | 36 | Changes in 0.1.0: 37 | 38 | * Partial support for reading and writing XML 39 | 40 | * Tutorial in doc directory 41 | 42 | * New functions and operators 43 | 44 | 45 | Changes in 0.0.4: 46 | 47 | 48 | First release: 0.0.1 49 | 50 | Local variables: 51 | mode: outline 52 | paragraph-separate: "[ ]*$" 53 | end: 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Connection-Set Algebra (CSA) 2 | ============================ 3 | 4 | This is a demonstration implementation in Python of the Connection-Set 5 | Algebra (Djurfeldt, Mikael (2012), Neuroinformatics) 6 | 7 | Code status 8 | =========== 9 | [![Build Status](https://travis-ci.org/INCF/csa.svg?branch=master)](https://travis-ci.org/INCF/csa) 10 | [![Coverage Status](https://coveralls.io/repos/github/INCF/csa/badge.svg?branch=master)](https://coveralls.io/github/INCF/csa?branch=master) 11 | 12 | Purpose 13 | ======= 14 | 15 | The CSA library provides elementary connection-sets and operators for 16 | combining them. It also provides an iteration interface to such 17 | connection-sets enabling efficient iteration over existing connections 18 | with a small memory footprint also for very large networks. The CSA 19 | can be used as a component of neuronal network simulators or other 20 | tools. 21 | 22 | See the following reference for more information: 23 | 24 | Mikael Djurfeldt (2012) "The Connection-set Algebra---A Novel 25 | Formalism for the Representation of Connectivity Structure in Neuronal 26 | Network Models" Neuroinformatics 10(3), 1539-2791, 27 | http://dx.doi.org/10.1007/s12021-012-9146-1 28 | 29 | License 30 | ======= 31 | 32 | CSA is released under the GNU General Public License 33 | 34 | Requirements 35 | ============ 36 | 37 | CSA is dependent on Numpy and Matplotlib 38 | 39 | Introduction 40 | ============ 41 | 42 | A connection set is a set of existing connections between a set of 43 | source nodes and a set of target nodes. Typically, source and target 44 | nodes are neurons in a neuronal network, but targets could also be 45 | particular structures of a neuron, such synaptic sites. Sources and 46 | targets are enumerated by integers and a connection is represented by 47 | a pair of integers, one denoting the source node and one the target 48 | node. Source and target can be (and is often) the same set. 49 | 50 | CSA connection sets are usually infinite. This is a simplification 51 | compared to the common situation of finite source and target sets in 52 | that the sizes of these sets do not need to be considered. Connection 53 | sets can have arbitrary values associated with connections. Pure 54 | connection sets without any values associated are called masks. 55 | 56 | Getting started 57 | =============== 58 | 59 | Basics 60 | ------ 61 | 62 | To get access to the CSA in Python, type: 63 | :: 64 | 65 | from csa import * 66 | 67 | The mask representing all possible connections between an infinite 68 | source and target set is: 69 | :: 70 | 71 | full 72 | 73 | To display a finite portion of the corresponding connectivity matrix, 74 | type: 75 | :: 76 | 77 | show (full) 78 | 79 | One-to-one connectivity (where source node 0 is connected to target 80 | node 0, source 1 to target 1 etc) is represented by the mask oneToOne: 81 | :: 82 | 83 | show (oneToOne) 84 | 85 | The default portion displayed by "show" is (0, 29) x (0, 29). 86 | (0, 99) x (0, 99) can be displayed using: 87 | :: 88 | 89 | show (oneToOne, 100, 100) 90 | 91 | If source and target set is the same, oneToOne describes 92 | self-connections. We can use CSA to compute the set of connections 93 | consisting of all possible connections except for self-connections 94 | using the set difference operator "-": 95 | :: 96 | 97 | show (full - oneToOne) 98 | 99 | Finite connection sets can be represented using either lists of 100 | connections, with connections represented as tuples: 101 | :: 102 | 103 | show ([(22, 7), (8, 23)]) 104 | 105 | or using the Cartesian product of intervals: 106 | :: 107 | 108 | show (cross (range (10), range (20))) 109 | 110 | We can form a finite version of the infinite oneToOne by taking the 111 | intersection "*" with a finite connection set: 112 | :: 113 | 114 | c = cross (range (10), range (10)) * oneToOne 115 | show (c) 116 | 117 | Finite connection sets can be tabulated: 118 | :: 119 | 120 | tabulate (c) 121 | 122 | In Python, finite connection sets provide an iterator interface: 123 | :: 124 | 125 | for x in cross (range (10), range (10)) * oneToOne: 126 | print x 127 | 128 | Random connectivity and the block operator 129 | ------------------------------------------ 130 | 131 | Connectivity where the existence of each possible connection is 132 | determined by a Bernoulli trial with probability p is expressed with 133 | the random mask random (p), e.g.: 134 | :: 135 | 136 | show (random (0.5)) 137 | 138 | The block operator expands each connection in the operand into a 139 | rectangular block in the resulting connection matrix, e.g.: 140 | :: 141 | 142 | show (block (5,3) * random (0.5)) 143 | 144 | Note that "*" here means operator application. There is also a 145 | quadratic version of the operator: 146 | :: 147 | 148 | show (block (10) * random (0.7)) 149 | 150 | Using intersection and set difference, we can now formulate a more 151 | complex mask: 152 | :: 153 | 154 | show (block (10) * random (0.7) * random (0.5) - oneToOne) 155 | 156 | Geometry 157 | -------- 158 | 159 | In CSA, the basic tool to handle distance dependent connectivity is 160 | metrics. Metrics are value sets d (i, j). Metrics can be defined 161 | through geometry functions. A geometry function maps an index to a 162 | position. We can, for example, assign a random position in the unit 163 | square to each index: 164 | :: 165 | 166 | g = random2d (900) 167 | 168 | The positions of the grid described by g have indices from 0 to 899 169 | and can be displayed like this: 170 | :: 171 | 172 | gplot2d (g, 900) 173 | 174 | Alternatively, we can arrange indices in a 30 x 30 grid within the 175 | unit square: 176 | :: 177 | 178 | g = grid2d (30) 179 | 180 | We can now define the euclidean metric on this grid: 181 | :: 182 | 183 | d = euclidMetric2d (g) 184 | 185 | An example of a distance dependent connection set is the disc mask 186 | Disc (r) * d which connects each index i to all indices j within a 187 | distance d (i, j) < r: 188 | :: 189 | 190 | c = disc (r) * d 191 | 192 | To examine the result we can employ the function gplotsel2d (g, c, i) 193 | which displays the targets g (j) of i in the connection set c: 194 | :: 195 | 196 | gplotsel2d (g, c, 434) 197 | 198 | In the case where the connection set represents a projection between 199 | two different coordinate systems, we define one geometry function for 200 | each. In the following example g1 is direction in visual space in arc 201 | minutes while g2 is position in the cortical representation of the 202 | Macaque fovea in mm: 203 | :: 204 | 205 | g1 = grid2d (30) 206 | g2 = grid2d (30, x0 = -7.0, xScale = 8.0, yScale = 8.0) 207 | 208 | We now define a projection operator which takes visual coordinates 209 | into cortical (Dow et al. 1985): 210 | :: 211 | 212 | import cmath 213 | 214 | @ProjectionOperator 215 | def GvspaceToCx (p): 216 | w = 7.7 * cmath.log (complex (p[0] + 0.33, p[1])) 217 | return (w.real, w.imag) 218 | 219 | To see how the grid g1 is transformed into cortical space, we type: 220 | :: 221 | 222 | gplot2d (GvspaceToCx * g1, 900) 223 | 224 | The inverse projection is defined: 225 | :: 226 | 227 | @ProjectionOperator 228 | def GcxToVspace (p): 229 | c = cmath.exp (complex (p[0], p[1]) / 7.7) - 0.33 230 | return (c.real, c.imag) 231 | 232 | Real receptive field sizes vary with eccentricity. Assume, for now, 233 | that we want to connect each target index to sources within a disc of 234 | constant radius. We then need to project back into visual space and 235 | use the disc operator: 236 | :: 237 | 238 | c = disc (0.1) * euclidMetric2d (g1, GcxToVspace * g2) 239 | 240 | Again, we use gplotsel2d to check the result: 241 | :: 242 | 243 | gplotsel2d (g2, c, 282) 244 | 245 | -------------------------------------------------------------------------------- /RELEASE: -------------------------------------------------------------------------------- 1 | Checklist for a release: 2 | 3 | * Install packages: 4 | 5 | dpkg-dev 6 | dpkg-dev-el 7 | lintian 8 | debhelper 9 | python-setuptools 10 | python-all 11 | python-nose 12 | python-numpy 13 | python-matplotlib 14 | 15 | * csa/version.py 16 | 17 | * (optional) debian/changelog 18 | 19 | * update NEWS 20 | 21 | * "Release x.y.z" in all ChangeLog:s 22 | 23 | * make debian-package 24 | 25 | * lintian -i python-csa_x.y.z-n_ARCH.changes 26 | 27 | * dupload python-csa_x.y.z-n_ARCH.changes 28 | 29 | * svn commit 30 | 31 | * svn cp http://svn.incf.org/svn/csa/trunk http://svn.incf.org/svn/csa/tags/release-x.y.z -m 'Tagging release x.y.z of CSA' 32 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Flip y-axis in show. Flip axes. 2 | 3 | * Go through how RNG:s are seeded, especially when one CS is used 4 | repeatedly as component of other CSs 5 | 6 | * Go through use of and rename classes Finite and FiniteMask. 7 | 8 | * Avoid intersection at top of MaskPartition 9 | 10 | * Implement CSetPartition 11 | 12 | * Implement lazy evaluation in BinaryCSet.makeValueSetMap 13 | 14 | * Implement SubMaskContainer (startIteration etc) to avoid code duplication 15 | 16 | * eliminate code duplication in _elementary.FanInRandomMask 17 | 18 | * loosen up criteria on Finite connection sets to BoundedConnectivity 19 | (Masks with upper bounds on the number of connections per source or 20 | target.) 21 | 22 | * generalize FanInRandomMask to work on BoundedInConnectivityMask:s, for 23 | example block masks of BoundedInConnectivityMask:s 24 | 25 | * generalize SampleNRandomMask to work on Finite masks 26 | 27 | * make value sets returned by value(c,k) iterable 28 | -------------------------------------------------------------------------------- /acinclude.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check whether the given FLAG works with the current language's compiler 12 | # or gives an error. (Warnings, however, are ignored) 13 | # 14 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on 15 | # success/failure. 16 | # 17 | # If EXTRA-FLAGS is defined, it is added to the current language's default 18 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 19 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 20 | # force the compiler to issue an error when a bad flag is given. 21 | # 22 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this 23 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. 24 | # 25 | # LICENSE 26 | # 27 | # Copyright (c) 2008 Guido U. Draheim 28 | # Copyright (c) 2011 Maarten Bosmans 29 | # 30 | # This program is free software: you can redistribute it and/or modify it 31 | # under the terms of the GNU General Public License as published by the 32 | # Free Software Foundation, either version 3 of the License, or (at your 33 | # option) any later version. 34 | # 35 | # This program is distributed in the hope that it will be useful, but 36 | # WITHOUT ANY WARRANTY; without even the implied warranty of 37 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 38 | # Public License for more details. 39 | # 40 | # You should have received a copy of the GNU General Public License along 41 | # with this program. If not, see . 42 | # 43 | # As a special exception, the respective Autoconf Macro's copyright owner 44 | # gives unlimited permission to copy, distribute and modify the configure 45 | # scripts that are the output of Autoconf when processing the Macro. You 46 | # need not follow the terms of the GNU General Public License when using 47 | # or distributing such scripts, even though portions of the text of the 48 | # Macro appear in them. The GNU General Public License (GPL) does govern 49 | # all other use of the material that constitutes the Autoconf Macro. 50 | # 51 | # This special exception to the GPL applies to versions of the Autoconf 52 | # Macro released by the Autoconf Archive. When you make and distribute a 53 | # modified version of the Autoconf Macro, you may extend this special 54 | # exception to the GPL to apply to your modified version as well. 55 | 56 | AC_DEFUN([AX_CHECK_COMPILE_FLAG], 57 | [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX 58 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl 59 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ 60 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS 61 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" 62 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], 63 | [AS_VAR_SET(CACHEVAR,[yes])], 64 | [AS_VAR_SET(CACHEVAR,[no])]) 65 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) 66 | AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], 67 | [m4_default([$2], :)], 68 | [m4_default([$3], :)]) 69 | AS_VAR_POPDEF([CACHEVAR])dnl 70 | ])dnl AX_CHECK_COMPILE_FLAGS 71 | -------------------------------------------------------------------------------- /aclocal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if test -z "$ACLOCAL" ; then 4 | for each in aclocal-1.14 aclocal-1.13 aclocal-1.12 aclocal-1.11 aclocal-1.10 aclocal-1.9 aclocal-1.8 aclocal-1.7 aclocal-1.6 aclocal ; do 5 | ACLOCAL=$each 6 | if test -n "`which $each 2>/dev/null`" ; then break ; fi 7 | done 8 | fi 9 | 10 | ACDIR=`which $ACLOCAL` 11 | ACDIR=`dirname $ACDIR` 12 | ACDIR=`dirname $ACDIR`/share/aclocal 13 | 14 | for each in $ACDIR ; do 15 | if test -d "$each" ; then 16 | AFLAGS="-I $each $AFLAGS" 17 | fi 18 | done 19 | 20 | echo $ACLOCAL $AFLAGS $@ 21 | $ACLOCAL $AFLAGS $@ 22 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [ -f autogen.sh ] || { 4 | echo "autogen.sh: run this command only at the top of the source tree." 5 | exit 1 6 | } 7 | 8 | if test -z "$AUTOMAKE" ; then 9 | for each in automake-1.14 automake-1.13 automake-1.12 automake-1.11 automake-1.10 automake-1.9 automake-1.8 automake-1.7 automake-1.6 automake ; do 10 | AUTOMAKE=$each 11 | if test -n "`which $each 2>/dev/null`" ; then break ; fi 12 | done 13 | fi 14 | 15 | ./aclocal.sh && 16 | echo libtoolize --copy --automake && 17 | libtoolize --copy --automake && 18 | echo autoheader && 19 | autoheader && 20 | echo autoconf && 21 | autoconf && 22 | echo $AUTOMAKE --copy --add-missing && 23 | $AUTOMAKE --copy --add-missing && 24 | echo Now run configure and make. 25 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce configure. 2 | AC_INIT(csa, 0.1.8) 3 | AM_INIT_AUTOMAKE 4 | AM_CONFIG_HEADER([config.h]) 5 | AM_MAINTAINER_MODE 6 | 7 | AC_MSG_CHECKING([library directory at]) 8 | eval pfx="/usr/local" 9 | if test "${exec_prefix}" != "NONE" ; then 10 | eval pfx="${exec_prefix}" 11 | elif test "${prefix}" != "NONE" ; then 12 | eval pfx="${prefix}" 13 | fi 14 | eval libdir="`echo "$libdir" | sed "s,\\${exec_prefix},${pfx},"`" 15 | AC_MSG_RESULT($libdir) 16 | 17 | # 18 | # Use libneurosim? 19 | # 20 | AC_CHECK_LIB(neurosim, libneurosim_version, HAVE_LIBNEUROSIM="auto", HAVE_LIBNEUROSIM="no") 21 | if test "x$HAVE_LIBNEUROSIM" = xauto; then 22 | LIBNEUROSIM_LIBS="-lneurosim" 23 | LIBNEUROSIM_PY_LIBS="-lpyneurosim" 24 | LIBNEUROSIM_INCLUDE="" 25 | fi 26 | AC_ARG_WITH(libneurosim, [ --with-libneurosim[[=directory]] Request the use of libneurosim. Optionally give the directory, where libneurosim is installed], 27 | [ 28 | if test "$withval" != "no"; then 29 | if test "$withval" != "yes"; then 30 | LIBNEUROSIM_LIBS="-L${withval}/lib -lneurosim" 31 | LIBNEUROSIM_PY_LIBS="-lpyneurosim" 32 | LIBNEUROSIM_INCLUDE="-I${withval}/include" 33 | fi 34 | HAVE_LIBNEUROSIM="yes" 35 | else 36 | HAVE_LIBNEUROSIM="no" 37 | fi 38 | ]) 39 | 40 | if test "x$HAVE_LIBNEUROSIM" != xno; then 41 | AC_DEFINE(HAVE_LIBNEUROSIM, 1, [libneurosim support enabled?]) 42 | fi 43 | 44 | AC_ARG_WITH([python], 45 | [AS_HELP_STRING([--without-python], [ignore the presence of Python and disable libpycsa])]) 46 | 47 | AS_IF([test "x$with_python" != "xno"], 48 | [AS_IF([test "x$with_python" != "xyes"], 49 | [PYTHON="$with_python"]) 50 | AM_PATH_PYTHON([2.6], [have_python=yes], [have_python=no])], 51 | [have_python=no]) 52 | 53 | AS_IF([test "x$have_python" = "xyes"], [ 54 | PYTHON_INC=`$PYTHON -c 'import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_python_inc())'` 55 | AC_CHECK_FILE(["${PYTHON_INC}/Python.h"], [LIBPYCSA_CPPFLAGS="-I${PYTHON_INC}"], [have_python=no]) 56 | PYTHONLIB="" 57 | AC_CHECK_LIB(python${am_cv_python_version}m,PyArg_ParseTuple, 58 | [PYTHONLIB=-lpython${am_cv_python_version}m], 59 | [AC_CHECK_LIB(python${am_cv_python_version},PyArg_ParseTuple, 60 | [PYTHONLIB=-lpython${am_cv_python_version}])]) 61 | ]) 62 | 63 | AC_MSG_CHECKING([whether to build libpycsa]) 64 | AS_IF([test "x$have_python" = "xyes"], [], 65 | [AS_IF([test "x$with_python" = "xyes"], 66 | [AC_MSG_ERROR([Libpycsa requested, but Python not found])])]) 67 | AC_MSG_RESULT([$have_python]) 68 | 69 | AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing], 70 | [LIBPYCSA_CXXFLAGS="-fno-strict-aliasing"], []) 71 | 72 | # FIXME: this means that --without-python make will not recurse into libpycsa subdir 73 | if test "x$have_python" = "xyes"; then 74 | LIBPYCSA_SUBDIR="libpycsa" 75 | else 76 | LIBPYCSA_SUBDIR="" 77 | fi 78 | 79 | AM_CONDITIONAL([HAVE_PYTHON], [test "x$have_python" = "xyes"]) 80 | 81 | AC_SUBST([HAVE_PYTHON]) 82 | AC_SUBST([LIBPYCSA_SUBDIR]) 83 | 84 | AC_SUBST([PYTHON]) 85 | AC_SUBST([PYTHONLIB]) 86 | 87 | AC_SUBST([LIBPYCSA_CPPFLAGS]) 88 | AC_SUBST([LIBPYCSA_CXXFLAGS]) 89 | 90 | AC_LANG(C++) 91 | 92 | AC_PROG_CXX 93 | AC_PROG_LIBTOOL 94 | 95 | AC_SUBST(LIBNEUROSIM_LIBS) 96 | AC_SUBST(LIBNEUROSIM_PY_LIBS) 97 | AC_SUBST(LIBNEUROSIM_INCLUDE) 98 | 99 | AC_CONFIG_FILES([ 100 | Makefile 101 | csa/__init__.py 102 | libpycsa/Makefile 103 | ]) 104 | 105 | AC_OUTPUT 106 | 107 | dnl Local Variables: 108 | dnl comment-start: "dnl " 109 | dnl comment-end: "" 110 | dnl comment-start-skip: "\\bdnl\\b\\s *" 111 | dnl End: 112 | -------------------------------------------------------------------------------- /continuous_integration/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | -------------------------------------------------------------------------------- /continuous_integration/test_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | python --version 6 | python -c "import numpy; print('numpy %s' % numpy.__version__)" 7 | python -c "import matplotlib; print('matplotlib %s' % matplotlib.__version__)" 8 | 9 | # build csa inplace 10 | python setup.py build_ext -i 11 | 12 | # run tests, but if mystery segmentation fault occurr, rerun tests as an 13 | # attempt at a clean exit 14 | while true; do 15 | nosetests --with-coverage --cover-package=csa 16 | if [ $? -eq 0 ] 17 | then 18 | exit 0 19 | break 20 | fi 21 | done -------------------------------------------------------------------------------- /csa/ChangeLog: -------------------------------------------------------------------------------- 1 | 2013-10-30 Mikael Djurfeldt 2 | 3 | * intervalset.py (IntervalSet.skipIntervals): New function. 4 | 5 | 2013-06-13 Mikael Djurfeldt 6 | 7 | * geometry.py (euclidToroidDistance2d, euclidToroidMetric2d): New 8 | functions. 9 | 10 | 2013-06-12 Mikael Djurfeldt 11 | 12 | * misc.py (repeat): New operator. 13 | 14 | * _misc.py (Repeat, RepeatMask): New masks. 15 | 16 | 2012-05-25 Mikael Djurfeldt 17 | 18 | * intervalset.py (IntervalSet.goodInterval): Accept long as 19 | index. 20 | 21 | 2012-03-30 Mikael Djurfeldt 22 | 23 | * Release 0.1.0 24 | 25 | 2012-02-24 Mikael Djurfeldt 26 | 27 | * csaobject.py (CSAObject.formalFromXML): New method. 28 | (CSAObject.from_xml): Handle binding operators. 29 | 30 | * closure.py: New file. 31 | 32 | * __init__.py: import closure.py. 33 | 34 | 2011-07-29 Mikael Djurfeldt 35 | 36 | * connset.py (CSetPartition): New class. 37 | 38 | 2011-01-18 Mikael Djurfeldt 39 | 40 | * elementary.py (vset): New function. 41 | 42 | 2010-07-25 Mikael Djurfeldt 43 | 44 | * misc.py (shift), _misc.py (Shift): Added shift operator. 45 | 46 | 2010-07-16 Mikael Djurfeldt 47 | 48 | * intervalset.py (ComplementaryIntervalSet): New class. 49 | 50 | 2010-06-29 Mikael Djurfeldt 51 | 52 | * elementary.py (partition): New function. 53 | 54 | * _elementary.py (MaskPartition, SampleNRandomMask): New masks. 55 | 56 | * _misc.py (Random): Implemenented parameter N. 57 | 58 | * connset.py (IntervalSetMask.multisetSum): New method. 59 | 60 | * intervalset.py (IntervalSet.union): New method. 61 | 62 | 2010-06-27 Mikael Djurfeldt 63 | 64 | * _misc.py (ValueSetRandomMask): New mask. Random operator now 65 | implemented. 66 | 67 | * misc.py (gaussian): New operator. 68 | 69 | 2010-06-25 Mikael Djurfeldt 70 | 71 | * connset.py (ExplicitCSet): New class which captures value sets 72 | before coercion so that these can be returned by value (c, k) when 73 | possible. 74 | 75 | * elementary.py (cset): Use ExplicitCSet. 76 | 77 | 2010-06-24 Mikael Djurfeldt 78 | 79 | * elementary.py (random): Instance of Random operator. 80 | 81 | * _misc.py (Random): New operator. 82 | 83 | * _elementary.py (RandomMask): Renamed from Random. 84 | 85 | 2010-06-19 Mikael Djurfeldt 86 | 87 | * connset.py (Mask.__mul__): Only use commutativity if second 88 | operand is a ConnectionSet. 89 | 90 | * connset.py, _elementary.py, _misc.py: Restructured handling of 91 | bounds. Raise RunTime exception on attempt to retrieve iterator 92 | over infinite mask. 93 | 94 | * connset.py, elementary.py (ExplicitMask): Renamed from FiniteMask. 95 | 96 | 2010-06-18 Mikael Djurfeldt 97 | 98 | * Start of ChangeLog 99 | 100 | 101 | -------------------------------------------------------------------------------- /csa/__init__.py.in: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012,2017 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | import ctypes 20 | try: 21 | _libpycsa_handle_ = ctypes.CDLL ('@libdir@/libpycsa.so') 22 | except OSError: 23 | None 24 | 25 | from .version import __version__ 26 | from .connset import Mask 27 | from .connset import ConnectionSet 28 | from .elementary import * 29 | #from operators import * 30 | #from arithmetic import * 31 | from .misc import * 32 | from .geometry import * 33 | from .csaobject import parse, from_xml 34 | from .plot import * 35 | from .closure import * 36 | from .conngen import * 37 | -------------------------------------------------------------------------------- /csa/_elementary.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | import random 20 | import numpy 21 | import copy 22 | 23 | from . import connset as cs 24 | from . import intervalset as iset 25 | 26 | from .csaobject import * 27 | 28 | class FullMask (cs.IntervalSetMask): 29 | tag = 'full' 30 | 31 | def __init__ (self): 32 | cs.IntervalSetMask.__init__ (self, iset.N, iset.N) 33 | self.name = FullMask.tag 34 | CSAObject.tag_map[CSA + FullMask.tag] = (self, SINGLETON) 35 | 36 | def __call__ (self, N0, N1 = None): 37 | if N1 == None: 38 | N1 = N0 39 | return cs.FiniteISetMask (iset.IntervalSet ((0, N0 - 1)), \ 40 | iset.IntervalSet ((0, N1 - 1))) 41 | 42 | 43 | class OneToOne (cs.Mask): 44 | tag = 'oneToOne' 45 | 46 | def __init__ (self): 47 | cs.Mask.__init__ (self) 48 | self.name = OneToOne.tag 49 | CSAObject.tag_map[CSA + OneToOne.tag] = (self, SINGLETON) 50 | 51 | def iterator (self, low0, high0, low1, high1, state): 52 | for i in range (max (low0, low1), min (high0, high1)): 53 | yield (i, i) 54 | 55 | 56 | class ConstantRandomMask (cs.Mask): 57 | tag = 'randomMask' 58 | 59 | def __init__ (self, p): 60 | cs.Mask.__init__ (self) 61 | self.p = p 62 | self.state = random.getstate () 63 | self.name = ConstantRandomMask.tag 64 | 65 | def startIteration (self, state): 66 | random.setstate (self.state) 67 | return self 68 | 69 | def iterator (self, low0, high0, low1, high1, state): 70 | for j in range (low1, high1): 71 | for i in range (low0, high0): 72 | if random.random () < self.p: 73 | yield (i, j) 74 | 75 | def repr (self): 76 | return 'random(%s)' % self.p 77 | 78 | def _to_xml (self): 79 | return CSAObject.apply (ConstantRandomMask.tag, self.p) 80 | 81 | registerTag (ConstantRandomMask.tag, ConstantRandomMask, 1) 82 | 83 | 84 | class SampleNRandomOperator (cs.Operator): 85 | tag = 'random_N' 86 | 87 | def __init__ (self, N): 88 | self.N = N 89 | 90 | def __mul__ (self, other): 91 | assert isinstance (other, cs.Finite) \ 92 | and isinstance (other, cs.Mask), \ 93 | 'expected finite mask' 94 | return SampleNRandomMask (self.N, other) 95 | 96 | def repr (self): 97 | return 'random(N = %s)' % self.N 98 | 99 | def _to_xml (self): 100 | return CSAObject.apply (SampleNRandomOperator.tag, self.N) 101 | 102 | registerTag (SampleNRandomOperator.tag, SampleNRandomOperator, 1) 103 | 104 | 105 | class SampleNRandomMask (cs.Finite,cs.Mask): 106 | # The algorithm based on first sampling the number of connections 107 | # per partition has been arrived at through discussions with Hans 108 | # Ekkehard Plesser. 109 | # 110 | def __init__ (self, N, mask): 111 | cs.Mask.__init__ (self) 112 | self.N = N 113 | assert isinstance (mask, cs.FiniteISetMask), \ 114 | 'SampleNRandomMask currently only operates on FiniteISetMask:s' 115 | self.mask = mask 116 | self.randomState = random.getstate () 117 | self.npRandomState = numpy.random.get_state () 118 | 119 | def bounds (self): 120 | return self.mask.bounds () 121 | 122 | def startIteration (self, state): 123 | obj = copy.copy (self) # local state: N, N0, perTarget, sources 124 | random.setstate (self.randomState) 125 | obj.isPartitioned = False 126 | if 'partitions' in state: 127 | obj.isPartitioned = True 128 | partitions = list (map (self.mask.intersection, state['partitions'])) 129 | sizes = list (map (len, partitions)) 130 | total = sum (sizes) 131 | 132 | # The following yields the same result on all processes. 133 | # We should add a seed function to the CSA. 134 | if 'seed' in state: 135 | seed = state['seed'] 136 | else: 137 | seed = 'SampleNRandomMask' 138 | # Numpy requires an unsigned 32-bit integer 139 | numpy.random.seed (hash (seed) % (numpy.iinfo(numpy.uint32).max + 1)) 140 | N = numpy.random.multinomial (self.N, numpy.array (sizes) \ 141 | / float (total)) 142 | obj.N = N[state['selected']] 143 | obj.mask = partitions[state['selected']] 144 | assert isinstance (obj.mask, cs.FiniteISetMask), \ 145 | 'SampleNRandomMask iterator only handles finite IntervalSetMask partitions' 146 | obj.mask = obj.mask.startIteration (state) 147 | obj.N0 = len (obj.mask.set0) 148 | obj.lastBound0 = False 149 | N1 = len (obj.mask.set1) 150 | numpy.random.set_state (self.npRandomState) 151 | obj.perTarget = numpy.random.multinomial (obj.N, [1.0 / N1] * N1) 152 | return obj 153 | 154 | def iterator (self, low0, high0, low1, high1, state): 155 | m = self.mask.set1.count (0, low1) 156 | 157 | if self.isPartitioned and m > 0: 158 | # "replacement" for a proper random.jumpahead (n) 159 | # This is required so that different partitions of this 160 | # mask aren't produced using the same stream of random 161 | # numbers. 162 | random.seed (random.getrandbits (32) + m) 163 | 164 | if self.lastBound0 != (low0, high0): 165 | self.lastBound0 = (low0, high0) 166 | self.sources = [] 167 | for i in self.mask.set0.boundedIterator (low0, high0): 168 | self.sources.append (i) 169 | 170 | nSources = len (self.sources) 171 | for j in self.mask.set1.boundedIterator (low1, high1): 172 | s = [] 173 | for k in range (0, self.perTarget[m]): 174 | i = random.randint (0, self.N0 - 1) 175 | if i < nSources: 176 | s.append (self.sources[i]) 177 | s.sort () 178 | for i in s: 179 | yield (i, j) 180 | m += 1 181 | 182 | def repr (self): 183 | return self._repr_applyop ('random(N=%s)' % self.N, self.mask) 184 | 185 | def _to_xml (self): 186 | return E ('apply', 187 | E ('times'), 188 | CSAObject.apply (SampleNRandomOperator.tag, self.N), 189 | self.mask._to_xml ()) 190 | 191 | 192 | class FanInRandomOperator (cs.Operator): 193 | tag = 'random_fanIn' 194 | 195 | def __init__ (self, fanIn): 196 | self.fanIn = fanIn 197 | 198 | def __mul__ (self, other): 199 | assert isinstance (other, cs.Finite) \ 200 | and isinstance (other, cs.Mask), \ 201 | 'expected finite mask' 202 | return FanInRandomMask (self.fanIn, other) 203 | 204 | def repr (self): 205 | return 'random(fanIn=%s)' % self.fanIn 206 | 207 | def _to_xml (self): 208 | return CSAObject.apply (FanInRandomOperator.tag, self.fanIn) 209 | 210 | registerTag (FanInRandomOperator.tag, FanInRandomOperator, 1) 211 | 212 | 213 | # This code is copied and modified from SampleNRandomMask 214 | # *fixme* refactor code and eliminate code duplication 215 | class FanInRandomMask (cs.Finite, cs.Mask): 216 | # The algorithm based on first sampling the number of connections 217 | # per partition has been arrived at through discussions with Hans 218 | # Ekkehard Plesser. 219 | # 220 | def __init__ (self, fanIn, mask): 221 | cs.Mask.__init__ (self) 222 | self.fanIn = fanIn 223 | assert isinstance (mask, cs.FiniteISetMask), \ 224 | 'FanInRandomMask currently only operates on FiniteISetMask:s' 225 | self.mask = mask 226 | self.randomState = random.getstate () 227 | 228 | def bounds (self): 229 | return self.mask.bounds () 230 | 231 | def startIteration (self, state): 232 | obj = copy.copy (self) # local state: N, N0, perTarget, sources 233 | random.setstate (self.randomState) 234 | obj.isPartitioned = False 235 | if 'partitions' in state: 236 | obj.isPartitioned = True 237 | partitions = list (map (self.mask.intersection, state['partitions'])) 238 | 239 | # The following yields the same result on all processes. 240 | # We should add a seed function to the CSA. 241 | if 'seed' in state: 242 | seed = state['seed'] 243 | else: 244 | seed = 'FanInRandomMask' 245 | # Numpy.random.seed requires an unsigned 32 bit integer 246 | numpy.random.seed (hash (seed) % (numpy.iinfo(numpy.uint32).max + 1)) 247 | 248 | selected = state['selected'] 249 | obj.mask = partitions[selected] 250 | assert isinstance (obj.mask, cs.FiniteISetMask), \ 251 | 'FanInRandomMask iterator only handles finite IntervalSetMask partitions' 252 | obj.mask = obj.mask.startIteration (state) 253 | obj.N0 = len (obj.mask.set0) 254 | obj.lastBound0 = False 255 | if obj.isPartitioned: 256 | obj.perTarget = [] 257 | for j in obj.mask.set1: 258 | size = 0 259 | sourceDist = numpy.zeros (len (partitions)) 260 | for k in range (len (partitions)): 261 | if j in partitions[k].set1: 262 | sourceDist[k] = len (partitions[k].set0) 263 | sourceDist /= sum (sourceDist) 264 | dist = numpy.random.multinomial (self.fanIn, sourceDist) 265 | obj.perTarget.append (dist[selected]) 266 | else: 267 | obj.perTarget = [self.fanIn] * len (obj.mask.set1) 268 | return obj 269 | 270 | def iterator (self, low0, high0, low1, high1, state): 271 | m = self.mask.set1.count (0, low1) 272 | 273 | if self.isPartitioned and m > 0: 274 | # "replacement" for a proper random.jumpahead (n) 275 | # This is required so that different partitions of this 276 | # mask aren't produced using the same stream of random 277 | # numbers. 278 | random.seed (random.getrandbits (32) + m) 279 | 280 | if self.lastBound0 != (low0, high0): 281 | self.lastBound0 = (low0, high0) 282 | self.sources = [] 283 | for i in self.mask.set0.boundedIterator (low0, high0): 284 | self.sources.append (i) 285 | 286 | nSources = len (self.sources) 287 | for j in self.mask.set1.boundedIterator (low1, high1): 288 | s = [] 289 | for k in range (0, self.perTarget[m]): 290 | i = random.randint (0, self.N0 - 1) 291 | if i < nSources: 292 | s.append (self.sources[i]) 293 | s.sort () 294 | for i in s: 295 | yield (i, j) 296 | m += 1 297 | 298 | def repr (self): 299 | return self._repr_applyop ('random(fanIn=%s)' % self.fanIn, self.mask) 300 | 301 | def _to_xml (self): 302 | return E ('apply', 303 | E ('times'), 304 | CSAObject.apply (FanInRandomOperator.tag, self.fanIn), 305 | self.mask._to_xml ()) 306 | 307 | 308 | class FanOutRandomOperator (cs.Operator): 309 | tag = 'random_fanOut' 310 | 311 | def __init__ (self, fanOut): 312 | self.fanOut = fanOut 313 | 314 | def __mul__ (self, other): 315 | assert isinstance (other, cs.Finite) \ 316 | and isinstance (other, cs.Mask), \ 317 | 'expected finite mask' 318 | return FanInRandomMask (self.fanOut, other.transpose ()).transpose () 319 | 320 | def repr (self): 321 | return 'random(fanOut=%s)' % self.fanOut 322 | 323 | def _to_xml (self): 324 | return CSAObject.apply (FanOutRandomOperator.tag, self.fanOut) 325 | 326 | registerTag (FanOutRandomOperator.tag, FanOutRandomOperator, 1) 327 | -------------------------------------------------------------------------------- /csa/_misc.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012,2019,2020 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | import math 20 | import random 21 | import copy 22 | #from scipy.spatial import KDTree 23 | 24 | from . import connset as cs 25 | from . import valueset as vs 26 | from . import _elementary 27 | 28 | from .csaobject import * 29 | 30 | class Random (cs.Operator): 31 | def __mul__ (self, valueSet): 32 | return ValueSetRandomMask (valueSet) 33 | 34 | def __call__ (self, p = None, N = None, fanIn = None, fanOut = None): 35 | if p != None: 36 | assert N == None and fanIn == None and fanOut == None, \ 37 | 'inconsistent parameters' 38 | return _elementary.ConstantRandomMask (p) 39 | elif N != None: 40 | assert fanIn == None and fanOut == None, \ 41 | 'inconsistent parameters' 42 | return _elementary.SampleNRandomOperator (N) 43 | elif fanIn != None: 44 | assert fanOut == None, \ 45 | 'inconsistent parameters' 46 | return _elementary.FanInRandomOperator (fanIn) 47 | elif fanOut != None: 48 | return _elementary.FanOutRandomOperator (fanOut) 49 | assert False, 'inconsistent parameters' 50 | 51 | 52 | class ValueSetRandomMask (cs.Mask): 53 | def __init__ (self, valueSet): 54 | cs.Mask.__init__ (self) 55 | self.valueSet = valueSet 56 | self.state = random.getstate () 57 | 58 | def startIteration (self, state): 59 | random.setstate (self.state) 60 | return self 61 | 62 | def iterator (self, low0, high0, low1, high1, state): 63 | for j in range (low1, high1): 64 | for i in range (low0, high0): 65 | if random.random () < self.valueSet (i, j): 66 | yield (i, j) 67 | 68 | def _to_xml (self): 69 | return CSAObject.apply ('times', 'random', self.valueSet._to_xml ()) 70 | 71 | 72 | class Disc (cs.Operator): 73 | def __init__ (self, r): 74 | self.r = r 75 | 76 | def __mul__ (self, metric): 77 | return DiscMask (self.r, metric) 78 | 79 | 80 | class DiscMask (cs.Mask): 81 | def __init__ (self, r, metric): 82 | cs.Mask.__init__ (self) 83 | self.r = r 84 | self.metric = metric 85 | 86 | def iterator (self, low0, high0, low1, high1, state): 87 | for j in range (low1, high1): 88 | for i in range (low0, high0): 89 | if self.metric (i, j) < self.r: 90 | yield (i, j) 91 | 92 | 93 | class Rectangle (cs.Operator): 94 | def __init__ (self, width, height): 95 | self.width = width 96 | self.height = height 97 | 98 | def __mul__ (self, gFunction): 99 | if isinstance (gFunction, tuple): 100 | return RectangleMask (self.width, self.height, 101 | gFunction[0], gFunction[1]) 102 | else: 103 | return RectangleMask (self.width, self.height, gFunction, gFunction) 104 | 105 | 106 | class RectangleMask (cs.Mask): 107 | def __init__ (self, width, height, g0, g1): 108 | cs.Mask.__init__ (self) 109 | self.hwidth = width / 2.0 110 | self.hheight = height / 2.0 111 | self.g0 = g0 112 | self.g1 = g1 113 | 114 | def iterator (self, low0, high0, low1, high1, state): 115 | for j in range (low1, high1): 116 | for i in range (low0, high0): 117 | p0 = self.g0 (i) 118 | p1 = self.g1 (j) 119 | dx = p0[0] - p1[0] 120 | dy = p0[1] - p1[1] 121 | if abs (dx) < self.hwidth and abs (dy) < self.hheight: 122 | yield (i, j) 123 | 124 | 125 | class Gaussian (cs.Operator): 126 | def __init__ (self, sigma, cutoff): 127 | cs.Operator.__init__ (self, 'gaussian') 128 | self.sigma = sigma 129 | self.cutoff = cutoff 130 | 131 | def __mul__ (self, metric): 132 | return GaussianValueSet (self, metric) 133 | 134 | 135 | class GaussianValueSet (OpExprValue, vs.ValueSet): 136 | def __init__ (self, operator, metric): 137 | OpExprValue.__init__ (self, operator, metric) 138 | self.sigma22 = 2 * operator.sigma * operator.sigma 139 | self.cutoff = operator.cutoff 140 | self.metric = metric 141 | 142 | def __call__ (self, i, j): 143 | d = self.metric (i, j) 144 | return math.exp (- d * d / self.sigma22) if d < self.cutoff else 0.0 145 | 146 | 147 | class Block (cs.Operator): 148 | def __init__ (self, M, N): 149 | self.M = M 150 | self.N = N 151 | 152 | def __mul__ (self, other): 153 | c = cs.coerceCSet (other) 154 | if isinstance (c, cs.Mask): 155 | return BlockMask (self.M, self.N, c) 156 | else: 157 | return cs.ConnectionSet (BlockCSet (self.M, self.N, c)) 158 | 159 | 160 | class BlockMask (cs.Mask): 161 | def __init__ (self, M, N, mask): 162 | cs.Mask.__init__ (self) 163 | self.M = M 164 | self.N = N 165 | self.m = mask 166 | 167 | def startIteration (self, state): 168 | #*fixme* filter out 'partitions' from state 169 | nState = {} 170 | for k in state: 171 | if k != 'partitions': 172 | nState[k] = state[k] 173 | self.obj = self.m.startIteration (nState) 174 | return self 175 | 176 | def iterator (self, low0, high0, low1, high1, state): 177 | maskIter = self.obj.iterator (low0 // self.M, 178 | (high0 + self.M - 1) // self.M, 179 | low1 // self.N, 180 | (high1 + self.N - 1) // self.N, 181 | state) 182 | try: 183 | pre = [] 184 | (i, j) = next (maskIter) 185 | while True: 186 | # collect connections in one connection matrix column 187 | post = j 188 | while j == post: 189 | pre.append (i) 190 | (i, j) = next (maskIter) 191 | 192 | # generate blocks for the column 193 | for jj in range (max (self.N * post, low1), 194 | min (self.N * (post + 1), high1)): 195 | for k in pre: 196 | for ii in range (max (self.M * k, low0), 197 | min (self.M * (k + 1), high0)): 198 | yield (ii, jj) 199 | pre = [] 200 | except StopIteration: 201 | if pre: 202 | # generate blocks for the last column 203 | for jj in range (max (self.N * post, low1), 204 | min (self.N * (post + 1), high1)): 205 | for k in pre: 206 | for ii in range (max (self.M * k, low0), 207 | min (self.M * (k + 1), high0)): 208 | yield (ii, jj) 209 | 210 | 211 | class Repeat (cs.Operator): 212 | def __init__ (self, M, N): 213 | self.M = M 214 | self.N = N 215 | 216 | def __mul__ (self, other): 217 | c = cs.coerceCSet (other) 218 | if isinstance (c, cs.Mask): 219 | return RepeatMask (self.M, self.N, c) 220 | else: 221 | return cs.ConnectionSet (RepeatCSet (self.M, self.N, c)) 222 | 223 | 224 | # Not fully implemented 225 | # Currently only handles cases where we iterate over an even number of 226 | # ocurrences of the template mask, both with regard to sources and targets 227 | # 228 | class RepeatMask (cs.Mask): 229 | def __init__ (self, M, N, mask): 230 | cs.Mask.__init__ (self) 231 | self.M = M 232 | self.N = N 233 | self.m = mask 234 | 235 | def iterator (self, low0, high0, low1, high1, state): 236 | try: 237 | jj = low1 238 | nextHigh1 = (low1 + self.N) / self.N * self.N 239 | while nextHigh1 <= high1: 240 | maskIter = self.m.iterator (0, 241 | self.M, 242 | 0, 243 | self.N, 244 | state) 245 | try: 246 | (i, j) = next (maskIter) 247 | post = j 248 | while post < self.N: 249 | pre = [] 250 | while j == post: 251 | pre.append (i) 252 | (i, j) = next (maskIter) 253 | ii = low0 254 | while ii < high0: 255 | for k in pre: 256 | yield (ii + k, jj + post) 257 | ii += self.M 258 | post = j 259 | except StopIteration: 260 | ii = low0 261 | while ii < high0: 262 | for k in pre: 263 | yield (ii + k, jj + post) 264 | ii += self.M 265 | jj = nextHigh1 266 | nextHigh1 += self.N 267 | except StopIteration: 268 | return 269 | 270 | 271 | class Transpose (cs.Operator): 272 | def __mul__ (self, other): 273 | c = cs.coerceCSet (other) 274 | if isinstance (c, cs.Mask): 275 | return other.transpose () 276 | else: 277 | return cs.ConnectionSet (other.transpose ()) 278 | 279 | 280 | class Shift (cs.Operator): 281 | def __init__ (self, M, N): 282 | self.M = M 283 | self.N = N 284 | 285 | def __mul__ (self, other): 286 | c = cs.coerceCSet (other) 287 | if isinstance (c, cs.Mask): 288 | return other.shift (self.M, self.N) 289 | else: 290 | return cs.ConnectionSet (other.shift (self.M, self.N)) 291 | 292 | 293 | class Fix (cs.Operator): 294 | def __mul__ (self, other): 295 | c = cs.coerceCSet (other) 296 | if isinstance (c, cs.Mask): 297 | return FixedMask (other) 298 | else: 299 | return cs.ConnectionSet (FixedCSet (other)) 300 | 301 | 302 | class FixedMask (cs.FiniteMask): 303 | def __init__ (self, mask): 304 | cs.FiniteMask.__init__ (self) 305 | ls = [] 306 | for c in mask: 307 | ls.append (c) 308 | self.connections = ls 309 | targets = list (map (cs.target, ls)) 310 | self.low0 = min (ls)[0] 311 | self.high0 = max (ls)[0] + 1 312 | self.low1 = min (targets) 313 | self.high1 = max (targets) + 1 314 | 315 | def iterator (self, low0, high0, low1, high1, state): 316 | if not self.isBoundedBy (low0, high0, low1, high1): 317 | return iter (self.connections) 318 | else: 319 | return self.boundedIterator (low0, high0, low1, high1) 320 | 321 | def boundedIterator (self, low0, high0, low1, high1): 322 | for c in self.connections: 323 | if low0 <= c[0] and c[0] < high0 \ 324 | and low1 <= c[1] and c[1] < high1: 325 | yield c 326 | -------------------------------------------------------------------------------- /csa/closure.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | from .csaobject import * 20 | 21 | import inspect 22 | 23 | try: 24 | from lxml import etree 25 | from lxml.builder import E 26 | except ImportError: 27 | pass 28 | 29 | class Closure (CSAObject): 30 | tag = 'closure' 31 | name = tag 32 | 33 | def __init__ (self, formals, e): 34 | self.formals = formals 35 | self.etree = e 36 | 37 | @staticmethod 38 | def formalToXML (formal): 39 | return E ('bvar', E ('ci', formal)) 40 | 41 | def _to_xml (self): 42 | formals = list (map (Closure.formalToXML, self.formals)) 43 | return E ('bind', E ('closure'), *formals + [ self.etree ]) 44 | 45 | def __call__ (self, *args): 46 | assert len (args) == len (self.formals), "arguments %s don't match formals %s" % (args, self.formals) 47 | bindings = {} 48 | for (formal, arg) in map (self.formals, args): 49 | bindings[formal] = arg 50 | return CSAObject.from_xml (self.etree, bindings) 51 | 52 | registerTag (Closure.tag, Closure, BINDOPERATOR) 53 | -------------------------------------------------------------------------------- /csa/conngen.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | try: 20 | from nineml.connection_generator import ConnectionGenerator 21 | HAVE_CG=True 22 | except ImportError: 23 | HAVE_CG=False 24 | 25 | if HAVE_CG: 26 | from .csaobject import from_xml 27 | from .elementary import arity, cross, partition 28 | from .closure import Closure 29 | 30 | class CSAConnectionGenerator (ConnectionGenerator): 31 | def __init__ (self, cset): 32 | self.cset = cset 33 | self.generator = False 34 | 35 | @property 36 | def arity (self): 37 | return arity (self.cset) 38 | 39 | def setMask (self, mask): 40 | self.setMasks ([mask], 0) 41 | 42 | def setMasks (self, masks, local): 43 | csaMasks = list (map (CSAConnectionGenerator.makeMask, masks)) 44 | self.generator = partition (self.cset, csaMasks, local) 45 | 46 | @staticmethod 47 | def makeMask (mask): 48 | return cross (CSAConnectionGenerator.makeIList (mask.sources), 49 | CSAConnectionGenerator.makeIList (mask.targets)) 50 | 51 | @staticmethod 52 | def makeIList (iset): 53 | if iset.skip == 1: 54 | return iset.intervals 55 | else: 56 | ls = [] 57 | for ivl in iset.intervals: 58 | for i in range (ivl[0], ivl[1] + 1, iset.skip): 59 | ls.append ((i, i)) 60 | return ls 61 | 62 | def __len__ (self): 63 | return self.generator.__len__ () 64 | 65 | def __iter__ (self): 66 | return self.generator.__iter__ () 67 | 68 | def connectionGeneratorClosureFromXML (element): 69 | cset = from_xml (element) 70 | if isinstance (cset, Closure): 71 | return lambda *args: CSAConnectionGenerator (cset (*args)) 72 | else: 73 | return lambda: CSAConnectionGenerator (cset) 74 | -------------------------------------------------------------------------------- /csa/csaobject.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | try: 20 | from lxml import etree 21 | from lxml.builder import E 22 | except ImportError: 23 | pass 24 | 25 | csa_tag = 'CSA' 26 | csa_namespace = 'http://software.incf.org/software/csa/1.0' 27 | CSA = '{%s}' % csa_namespace 28 | 29 | CUSTOM = -4 30 | OPERATOR = -3 31 | BINDOPERATOR = -2 32 | SINGLETON = -1 33 | 34 | def to_xml (obj): 35 | if isinstance (obj, str): 36 | return E (obj) 37 | elif isinstance (obj, (int, float)): 38 | return E ('cn', str (obj)) 39 | elif isinstance (obj, CSAObject): 40 | return obj._to_xml () 41 | else: 42 | raise RuntimeError ("don't know how to turn %s into xml" % obj) 43 | 44 | # precedence levels: 45 | # 46 | # 0 + - 47 | # 1 * 48 | # 2 ~ 49 | 50 | class CSAObject (object): 51 | tag_map = {} 52 | 53 | def __init__ (self, name, precedence = 3): 54 | self.name = name 55 | self.precedence = precedence 56 | 57 | def __repr__ (self): 58 | return 'CSA(%s)' % self.repr () 59 | 60 | def repr (self): 61 | if hasattr (self, 'name'): 62 | return self.name 63 | else: 64 | return self.__class__.__name__ 65 | 66 | def _repr_as_op2 (self, parentPrecedence): 67 | if self.precedence <= parentPrecedence: 68 | return '(%s)' % self.repr () 69 | else: 70 | return self.repr () 71 | 72 | def _repr_applyop (self, op_repr, obj): 73 | return '%s*%s' % (op_repr, obj._repr_as_op2 (1)) 74 | 75 | def to_xml (self): 76 | return E (csa_tag, self._to_xml (), xmlns=csa_namespace) 77 | 78 | def _to_xml (self): 79 | return E (self.name) 80 | 81 | @classmethod 82 | def apply (cls, operator, *operands): 83 | return E ('apply', to_xml (operator), *list (map (to_xml, operands))) 84 | 85 | @classmethod 86 | def formalFromXML (cls, element): 87 | assert element.tag == CSA + 'bvar' 88 | nodes = element.getchildren () 89 | assert nodes[0].tag == CSA + 'ci' 90 | return nodes[0].text 91 | 92 | @classmethod 93 | def from_xml (cls, element, env = {}): 94 | if element.tag == CSA + 'cn': 95 | return eval (element.text) 96 | elif element.tag == CSA + 'ci': 97 | #*fixme* Implement env as lists of dictionaries 98 | return env[element.text] 99 | elif element.tag == CSA + 'apply': 100 | nodes = element.getchildren () 101 | operator = nodes[0].tag 102 | operands = [ cls.from_xml (e, env) for e in nodes[1:] ] 103 | if operator == CSA + 'plus': 104 | return operands[0].__add__ (operands[1]) 105 | elif operator == CSA + 'minus': 106 | return operands[0].__sub__ (operands[1]) 107 | elif operator == CSA + 'times': 108 | return operands[0].__mul__ (operands[1]) 109 | elif operator == CSA + 'complement': 110 | return operands[0].__invert__ () 111 | else: 112 | # Function or operator application 113 | entry = CSAObject.tag_map[operator] 114 | obj = entry[0] 115 | if entry[1] == OPERATOR: 116 | return obj * operands[1] 117 | else: 118 | return obj (*operands) 119 | elif element.tag == CSA + 'bind': 120 | nodes = element.getchildren () 121 | tag = nodes[0].tag 122 | entry = CSAObject.tag_map[tag] 123 | if entry[1] != BINDOPERATOR: 124 | raise RuntimeError ("unknown binding operator tag %s" % tag) 125 | bindingOperator = entry[0] 126 | bvars = [ CSAObject.formalFromXML (e) for e in nodes[1:-1] ] 127 | return bindingOperator (bvars, nodes[-1]) 128 | elif element.tag in CSAObject.tag_map: 129 | entry = CSAObject.tag_map[element.tag] 130 | obj = entry[0] 131 | if entry[1] == SINGLETON: 132 | return obj 133 | elif entry[1] == CUSTOM: 134 | return obj.from_xml (element, env) 135 | else: 136 | return obj () 137 | else: 138 | raise RuntimeError ("don't know how parse tag %s" % element.tag) 139 | 140 | def xml (e): 141 | print(etree.tostring (e)) 142 | 143 | def write(self, file): 144 | doc = self.to_xml () 145 | etree.ElementTree(doc).write (file, encoding="UTF-8", 146 | pretty_print=True, xml_declaration=True) 147 | 148 | class BinaryCSAObject (CSAObject): 149 | operator_table = {'+': 'plus', '-': 'minus', '*': 'times'} 150 | 151 | def __init__ (self, name, op1, op2, precedence = 0): 152 | CSAObject.__init__ (self, name, precedence) 153 | self.op1 = op1 154 | self.op2 = op2 155 | 156 | def repr (self): 157 | if isinstance (self.op1, CSAObject): 158 | op1 = self.op1.repr () 159 | if self.op1.precedence < self.precedence: 160 | op1 = "(%s)" % op1 161 | else: 162 | op1 = self.op1 163 | 164 | if isinstance (self.op2, CSAObject): 165 | op2 = self.op2._repr_as_op2 (self.precedence) 166 | else: 167 | op2 = self.op2 168 | 169 | return "%s%s%s" % (op1, self.name, op2) 170 | 171 | def _to_xml (self): 172 | if isinstance (self.op1, CSAObject): 173 | op1 = self.op1._to_xml () 174 | else: 175 | op1 = self.op1 176 | 177 | if isinstance (self.op2, CSAObject): 178 | op2 = self.op2._to_xml () 179 | else: 180 | op2 = self.op2 181 | 182 | if self.name in BinaryCSAObject.operator_table: 183 | op = BinaryCSAObject.operator_table[self.name] 184 | else: 185 | op = self.name 186 | return E ('apply', E (op), op1, op2) 187 | 188 | 189 | class OpExprValue (BinaryCSAObject): 190 | def __init__ (self, operator, operand): 191 | BinaryCSAObject.__init__ (self, '*', operator, operand, 1) 192 | 193 | 194 | class Operator (CSAObject): 195 | def __init__ (self, name='ioperator'): 196 | CSAObject.__init__ (self, name) 197 | 198 | 199 | def from_xml (root): 200 | assert root.nsmap[None] == csa_namespace 201 | return CSAObject.from_xml (root.getchildren ()[0]) 202 | 203 | 204 | def parse (filename): 205 | doc = etree.parse (filename) 206 | return from_xml (doc.getroot()) 207 | 208 | 209 | def parseString (string): 210 | el = etree.fromstring (string) 211 | return from_xml (el) 212 | 213 | 214 | def registerTag (tag, obj, mode): 215 | CSAObject.tag_map[CSA + tag] = (obj, mode) 216 | 217 | -------------------------------------------------------------------------------- /csa/elementary.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | from __future__ import print_function 20 | import sys as _sys 21 | 22 | from . import intervalset as _iset 23 | from . import connset as _cs 24 | from . import valueset as _vs 25 | from . import _elementary 26 | from . import _misc 27 | from .csaobject import registerTag 28 | 29 | # Connection-Set constructor 30 | # 31 | def cset (mask, *valueSets): 32 | if valueSets: 33 | c = _cs.ExplicitCSet (mask, *valueSets) 34 | return _cs.ConnectionSet (c) 35 | else: 36 | return mask 37 | 38 | registerTag (_cs.CSet.tag, cset, 1) 39 | 40 | # Selectors 41 | # 42 | def mask (obj): 43 | cset = _cs.coerceCSet (obj) 44 | return cset.mask () 45 | 46 | def value (obj, k): 47 | assert isinstance (obj, _cs.ConnectionSet), 'expected connection-set' 48 | return obj.c.value (k) 49 | 50 | def arity (obj): 51 | if isinstance (obj, _cs.ConnectionSet): 52 | return obj.c.arity 53 | else: 54 | return 0 55 | 56 | # Value-set constructor 57 | # 58 | def vset (obj): 59 | if not callable (obj): 60 | return _vs.QuotedValueSet (obj) 61 | else: 62 | return _vs.GenericValueSet (obj) 63 | 64 | # Intervals 65 | # 66 | def ival (beg, end): 67 | return _iset.IntervalSet ((beg, end)) 68 | 69 | N = _iset.N 70 | 71 | # Cartesian product 72 | # 73 | def cross (set0, set1): 74 | return _cs.intervalSetMask (set0, set1) 75 | 76 | # Elementary masks 77 | # 78 | empty = cross ([], []) 79 | 80 | full = _elementary.FullMask () 81 | 82 | oneToOne = _elementary.OneToOne () 83 | 84 | random = _misc.Random () 85 | 86 | # Support for parallel simulator 87 | # 88 | def partition (c, masks, selected, seed = None): 89 | if isinstance (c, _cs.Mask): 90 | return _cs.MaskPartition (c, masks, selected, seed) 91 | elif isinstance (c, _cs.ConnectionSet): 92 | return _cs.ConnectionSet (_cs.CSetPartition (c, masks, selected, seed)) 93 | 94 | # Utilities 95 | # 96 | def tabulate (c): 97 | for x in c: 98 | print(u'{}'.format(x[0]), end=u' ') 99 | for e in x[1:]: 100 | print(u'\t{}'.format(e)) 101 | 102 | #del _elementary, cs, sys # not for export 103 | -------------------------------------------------------------------------------- /csa/geometry.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012,2019 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | import math as _math 20 | import random as _random 21 | import numpy as _numpy 22 | 23 | from . import intervalset as _iset 24 | 25 | def grid2d (width, xScale = 1.0, yScale = 1.0, x0 = 0.0, y0 = 0.0): 26 | xScale /= width 27 | yScale /= width 28 | g = lambda i: \ 29 | (x0 + xScale * (i % width), y0 + yScale * (i // width)) 30 | g.type = 'grid' 31 | g.width = width 32 | g.xScale = xScale 33 | g.yScale = yScale 34 | g.x0 = x0 35 | g.y0 = y0 36 | g.inverse = lambda x, y: \ 37 | int (round (x / xScale - x0)) \ 38 | + width * int (round (y / yScale - y0)) 39 | return g 40 | 41 | def random2d (N, xScale = 1.0, yScale = 1.0): 42 | coords = [(xScale * _random.random (), yScale * _random.random ()) 43 | for i in range (0, N)] 44 | g = lambda i: coords[i] 45 | g.type = 'ramdom' 46 | g.N = N 47 | g.xScale = xScale 48 | g.yScale = yScale 49 | # We should use a KD-tree here 50 | g.inverse = lambda x, y, domain=_iset.IntervalSet ((0, N - 1)): \ 51 | _numpy.array ([euclidDistance2d ((x, y), g(i)) \ 52 | for i in domain]).argmin () \ 53 | + domain.min () 54 | return g 55 | 56 | class ProjectionOperator (object): 57 | def __init__ (self, projection): 58 | self.projection = projection 59 | 60 | def __mul__ (self, g): 61 | projection = self.projection 62 | return lambda i: projection (g (i)) 63 | 64 | def euclidDistance2d (p1, p2): 65 | dx = p1[0] - p2[0] 66 | dy = p1[1] - p2[1] 67 | return _math.sqrt (dx * dx + dy * dy) 68 | 69 | def euclidMetric2d (g1, g2 = None): 70 | g2 = g1 if g2 == None else g2 71 | return lambda i, j: euclidDistance2d (g1 (i), g2 (j)) 72 | 73 | # These functions were contributed by Dr. Birgit Kriener 74 | 75 | def euclidToroidDistance2d (p1, p2, xScale=1.0, yScale=1.0): 76 | ddx, ddy = abs (p1[0] - p2[0]), abs (p1[1] - p2[1]) 77 | dx = ddx if ddx < xScale/2. else xScale - ddx 78 | dy = ddy if ddy < yScale/2. else yScale - ddy 79 | return _math.sqrt (dx * dx + dy * dy) 80 | 81 | def euclidToroidMetric2d (g1, g2 = None, xScale=1.0, yScale=1.0): 82 | g2 = g1 if g2 == None else g2 83 | return lambda i, j: euclidToroidDistance2d (g1 (i), g2 (j), xScale, yScale) 84 | 85 | # 3D functions 86 | 87 | def grid3d(width, xScale = 1.0, yScale = 1.0, zScale = 1.0, x0 = 0.0, y0 = 0.0, z0 = 0.0): 88 | """Returns a 3D grid between (0, 0, 0) and (1, 1, 1) 89 | :param width: The number of rows/columns the grid has 90 | :type width: int 91 | :param xScale: Scales the grid along the x axis 92 | :type xScale: float 93 | :param yScale: Scales the grid along the y axis 94 | :type yScale: float 95 | :param zScale: Scales the grid along the z axis 96 | :type zScale: float 97 | :param x0: Translates the grid along the x axis 98 | :type xScale: float 99 | :param y0: Translates the grid along the y axis 100 | :type yScale: float 101 | :param z0: Translates the grid along the z axis 102 | :type zScale: float 103 | :return: A callable grid that returns 3d positions when given an index""" 104 | xScale /= width 105 | yScale /= width 106 | zScale /= width 107 | g = lambda i: \ 108 | (x0 + xScale * (i % width), y0 + yScale * ((i % (width*width)) / width), z0 + zScale * (i / (width*width))) 109 | g.type = 'grid3d' 110 | g.width = width 111 | g.xScale = xScale 112 | g.yScale = yScale 113 | g.zScale = zScale 114 | g.x0 = x0 115 | g.y0 = y0 116 | g.z0 = z0 117 | g.inverse = lambda x, y, z: \ 118 | int (round (x / xScale - x0)) \ 119 | + width * (int (round (y / yScale - y0) 120 | + width * int (round (z / zScale - z0)))) 121 | return g 122 | 123 | def random3d(N, xScale = 1.0, yScale = 1.0, zScale = 1.0): 124 | """Creates a set of points scattered uniformly inside a 3D box 125 | :param N: Number of 3D points 126 | :type N: int 127 | :param xScale: The scale of the box on the x axis 128 | :type xScale: float 129 | :param yScale: The scale of the box on the y axis 130 | :type yScale: float 131 | :param zScale: The scale of the box on the z axis 132 | :type zScale: float 133 | """ 134 | coords = _numpy.random.random((N, 3)) 135 | coords[...,0] *= xScale 136 | coords[...,1] *= yScale 137 | coords[...,2] *= zScale 138 | g = lambda i: coords[i] 139 | g.type = 'random' 140 | g.N = N 141 | g.xScale = xScale 142 | g.yScale = yScale 143 | g.zScale = zScale 144 | g.inverse = lambda x, y, z, domain=_iset.IntervalSet ((0, N - 1)): \ 145 | _numpy.array ([euclidDistance3d (_numpy.array((x, y, z)), g(i)) \ 146 | for i in domain]).argmin () \ 147 | + domain.min () 148 | return g 149 | 150 | def euclidDistance3d(p1, p2): 151 | """Returns the euclidean distance in 3D between two points 152 | :param p1: The first point 153 | :type p1: numpy.array((3)) 154 | :param p2: The second point 155 | :type p2: numpy.array((3)) 156 | :return: The euclidean distance 157 | :rtype: float 158 | """ 159 | return _numpy.linalg.norm(p2 - p1) 160 | 161 | def euclidMetric3d (g1, g2 = None): 162 | """Returns an euclidean metric for 3D points 163 | :param g1: The first group of points 164 | :type g1: callable 165 | :param g2: The second group of points. If None, the first group of points is used 166 | :type g2: callable 167 | :return: A 3D euclidean metric function 168 | :rtype: function 169 | """ 170 | g2 = g1 if g2 == None else g2 171 | return lambda i, j: euclidDistance3d (g1 (i), g2 (j)) 172 | -------------------------------------------------------------------------------- /csa/intervalset.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012,2020 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | import sys 20 | 21 | from .csaobject import * 22 | 23 | infinity = sys.maxsize - 1 24 | 25 | # Interval sets are represented as ordered lists of closed intervals 26 | # 27 | class IntervalSet (CSAObject): 28 | tag = 'intervalset' 29 | 30 | @staticmethod 31 | # return true if tuple i represents a well-formed interval 32 | def goodInterval (i): 33 | return len (i) == 2 \ 34 | and isinstance (i[0], int) \ 35 | and isinstance (i[1], int) \ 36 | and i[0] <= i[1] 37 | 38 | @staticmethod 39 | def rangeToIntervals (x): 40 | if not x: 41 | return [] 42 | elif len (x) == 1: 43 | return [(x[0], x[0])] 44 | elif x[1] - x[0] == 1: 45 | return [(x[0], x[-1])] 46 | else: 47 | return ((e, e) for e in x) 48 | 49 | @staticmethod 50 | def coerce (s): 51 | if not isinstance (s, list): 52 | s = [ s ] 53 | 54 | res = [] 55 | for x in s: 56 | if isinstance (x, tuple): 57 | assert IntervalSet.goodInterval (x), 'malformed interval' 58 | res.append (x) 59 | elif isinstance (x, int): 60 | res.append ((x, x)) 61 | elif isinstance (x, range): 62 | res += IntervalSet.rangeToIntervals (x) 63 | else: 64 | raise TypeError ("can't interpret element as interval") 65 | s = res 66 | 67 | s.sort () 68 | 69 | # merge intervals 70 | # by construction we know that i[0] <= i[1] for i in s 71 | res = [] 72 | N = 0 73 | if s: 74 | lastLower = s[0][0] 75 | lastUpper = s[0][1] 76 | 77 | assert lastLower >= 0, 'only positive values allowed' 78 | 79 | for i in s[1:]: 80 | assert lastLower < i[0] and lastUpper < i[0], 'intervals overlap' 81 | if i[0] - lastUpper == 1: 82 | lastUpper = i[1] 83 | else: 84 | res.append ((lastLower, lastUpper)) 85 | N += 1 + lastUpper - lastLower 86 | lastLower = i[0] 87 | lastUpper = i[1] 88 | res.append ((lastLower, lastUpper)) 89 | N += 1 + lastUpper - lastLower 90 | 91 | return (res, N) 92 | 93 | def __init__ (self, s = [], intervals = None, nIntegers = None): 94 | if intervals: 95 | self.intervals = intervals 96 | self.nIntegers = nIntegers 97 | else: 98 | (self.intervals, self.nIntegers) = self.coerce (s) 99 | 100 | def repr (self): 101 | return 'IntervalSet(%r)' % self.intervals 102 | 103 | def __len__ (self): 104 | return self.nIntegers 105 | 106 | def __contains__ (self, n): 107 | for i in self.intervals: 108 | if n > i[1]: 109 | continue 110 | elif n >= i[0]: 111 | return True 112 | else: 113 | return False 114 | return False 115 | 116 | def __iter__ (self): 117 | for i in self.intervals: 118 | for e in range (i[0], i[1] + 1): 119 | yield e 120 | 121 | def __invert__ (self): 122 | return ComplementaryIntervalSet (intervals = self.intervals, \ 123 | nIntegers = self.nIntegers) 124 | 125 | def __add__ (self, other): 126 | if not isinstance (other, IntervalSet): 127 | other = IntervalSet (other) 128 | return self.union (other) 129 | 130 | def __radd__ (self, other): 131 | return IntervalSet (other).union (self) 132 | 133 | def __sub__ (self, other): 134 | if not isinstance (other, IntervalSet): 135 | other = IntervalSet (other) 136 | return self.intersection (~other) 137 | 138 | def __rsub__ (self, other): 139 | return IntervalSet (other).intersection (~self) 140 | 141 | def __mul__ (self, other): 142 | if not isinstance (other, IntervalSet): 143 | other = IntervalSet (other) 144 | return self.intersection (other) 145 | 146 | def __rmul__ (self, other): 147 | return IntervalSet (other).intersection (self) 148 | 149 | def finite (self): 150 | return True 151 | 152 | def shift (self, N): 153 | if not self or N == 0: 154 | return self 155 | 156 | intervals = [] 157 | nIntegers = self.nIntegers 158 | for (i, j) in self.intervals: 159 | i += N 160 | j += N 161 | if i >= 0: 162 | intervals.append ((i, j)) 163 | elif j >= 0: 164 | intervals.append ((0, j)) 165 | nIntegers += i 166 | 167 | return IntervalSet (intervals = intervals, nIntegers = nIntegers) 168 | 169 | def intervalIterator (self): 170 | return iter (self.intervals) 171 | 172 | def boundedIterator (self, low, high): 173 | iterator = iter (self.intervals) 174 | try: 175 | i = next (iterator) 176 | while i[1] < low: 177 | i = next (iterator) 178 | while i[0] < high: 179 | for e in range (max (low, i[0]), min (i[1] + 1, high)): 180 | yield e 181 | i = next (iterator) 182 | except StopIteration: 183 | return 184 | 185 | 186 | def count (self, low, high): 187 | iterator = iter (self.intervals) 188 | c = 0 189 | try: 190 | i = next (iterator) 191 | while i[1] < low: 192 | i = next (iterator) 193 | while i[0] < high: 194 | c += min (i[1] + 1, high) - max (low, i[0]) 195 | i = next (iterator) 196 | except StopIteration: 197 | pass 198 | return c 199 | 200 | def min (self): 201 | return self.intervals[0][0] 202 | 203 | def max (self): 204 | return self.intervals[-1][1] 205 | 206 | def skipIntervals (self): 207 | if len (self.intervals) <= 1 or self.intervals[0][0] != self.intervals[0][1]: 208 | return 1, self.intervals 209 | skip = self.intervals[1][0] - self.intervals[0][0] 210 | res = [] 211 | start = last = self.intervals[0][0] 212 | for i in self.intervals[1:]: 213 | if i[0] != i[1]: 214 | return 1, self.intervals 215 | if i[0] != last + skip: 216 | if i[0] % skip != 0: 217 | return 1, self.intervals 218 | res.append ((start, last)) 219 | start = i[0] 220 | last = i[0] 221 | res.append ((start, last)) 222 | return skip, res 223 | 224 | def intersection (self, other): 225 | res = [] 226 | N = 0 227 | iter0 = self.intervalIterator () 228 | iter1 = other.intervalIterator () 229 | try: 230 | i0 = next (iter0) 231 | i1 = next (iter1) 232 | while True: 233 | if i0[1] <= i1[1]: 234 | if i0[1] >= i1[0]: 235 | lower = max (i0[0], i1[0]) 236 | res.append ((lower, i0[1])) 237 | N += 1 + i0[1] - lower 238 | i0 = next (iter0) 239 | else: 240 | if i1[1] >= i0[0]: 241 | lower = max (i0[0], i1[0]) 242 | res.append ((lower, i1[1])) 243 | N += 1 + i1[1] - lower 244 | i1 = next (iter1) 245 | except StopIteration: 246 | pass 247 | iset = IntervalSet () 248 | iset.intervals = res 249 | iset.nIntegers = N 250 | return iset 251 | 252 | def union (self, other): 253 | if isinstance (other, ComplementaryIntervalSet): 254 | return ~(~self).intersection (~other) 255 | 256 | iset = IntervalSet () 257 | if not other.nIntegers: 258 | iset.intervals = list (self.intervals) 259 | iset.nIntegers = self.nIntegers 260 | return iset 261 | if not self.nIntegers: 262 | iset.intervals = list (other.intervals) 263 | iset.nIntegers = other.nIntegers 264 | return iset 265 | res = [] 266 | N = 0 267 | iter0 = self.intervalIterator () 268 | iter1 = other.intervalIterator () 269 | i0 = next (iter0) 270 | i1 = next (iter1) 271 | if i0[0] <= i1[0]: 272 | (lower, upper) = i0 273 | else: 274 | (lower, upper) = i1 275 | try: 276 | while True: 277 | if i0[0] <= i1[0]: 278 | if i0[0] <= upper + 1: 279 | if i0[1] > upper: 280 | upper = i0[1] 281 | else: 282 | res.append ((lower, upper)) 283 | N += 1 + upper - lower 284 | (lower, upper) = i0 285 | try: 286 | i0 = next (iter0) 287 | except StopIteration: 288 | if i1[0] <= upper + 1: 289 | if i1[1] > upper: 290 | upper = i1[1] 291 | i1 = (lower, upper) 292 | else: 293 | res.append ((lower, upper)) 294 | N += 1 + upper - lower 295 | while True: 296 | res.append (i1) 297 | N += 1 + i1[1] - i1[0] 298 | i1 = next (iter1) 299 | else: 300 | if i1[0] <= upper + 1: 301 | if i1[1] > upper: 302 | upper = i1[1] 303 | else: 304 | res.append ((lower, upper)) 305 | N += 1 + upper - lower 306 | (lower, upper) = i1 307 | try: 308 | i1 = next (iter1) 309 | except StopIteration: 310 | if i0[0] <= upper + 1: 311 | if i0[1] > upper: 312 | upper = i0[1] 313 | i0 = (lower, upper) 314 | else: 315 | res.append ((lower, upper)) 316 | N += 1 + upper - lower 317 | while True: 318 | res.append (i0) 319 | N += 1 + i0[1] - i0[0] 320 | i0 = next (iter0) 321 | except StopIteration: 322 | pass 323 | iset.intervals = res 324 | iset.nIntegers = N 325 | return iset 326 | 327 | def _to_xml (self): 328 | intervals = [ E ('interval', E ('cn', str (i)), E ('cn', str (j))) 329 | for (i, j) in self.intervals ] 330 | return E (IntervalSet.tag, *intervals) 331 | 332 | @classmethod 333 | def from_xml (cls, element, env = {}): 334 | intervals = [] 335 | for ivalElement in element.getchildren (): 336 | ival = ivalElement.getchildren () 337 | intervals.append ((int (ival[0].text), int (ival[1].text))) 338 | return IntervalSet (intervals) 339 | 340 | CSAObject.tag_map[CSA + IntervalSet.tag] = (IntervalSet, CUSTOM) 341 | 342 | 343 | class ComplementaryIntervalSet (IntervalSet): 344 | def __init__ (self, s = [], intervals = None, nIntegers = None): 345 | IntervalSet.__init__ (self, s, intervals, nIntegers) 346 | 347 | def repr (self): 348 | if not self.intervals: 349 | return 'N' 350 | else: 351 | return '~IntervalSet(%r)' % self.intervals 352 | 353 | def __bool__ (self): 354 | return True 355 | 356 | # def __len__ (self): 357 | # raise RuntimeError ('ComplementaryIntervalSet has infinite length') 358 | 359 | def __contains__ (self, n): 360 | for i in self.intervals: 361 | if n < i[0]: 362 | continue 363 | elif n <= i[1]: 364 | return False 365 | else: 366 | return True 367 | return True 368 | 369 | def __iter__ (self): 370 | raise RuntimeError ("can't interate over ComplementaryIntervalSet") 371 | 372 | def __invert__ (self): 373 | return IntervalSet (intervals = self.intervals, \ 374 | nIntegers = self.nIntegers) 375 | 376 | def finite (self): 377 | return False 378 | 379 | def shift (self, N): 380 | iset = IntervalSet (intervals = self.intervals, \ 381 | nIntegers = self.nIntegers).shift (N) 382 | return ComplementaryIntervalSet (intervals = iset.intervals, \ 383 | nIntegers = iset.nIntegers) 384 | 385 | def intervalIterator (self): 386 | start = 0 387 | for i in self.intervals: 388 | if i[0] > 0: 389 | yield (start, i[0] - 1) 390 | start = i[1] + 1 391 | yield (start, infinity) 392 | 393 | def boundedIterator (self, low, high): 394 | raise RuntimeError ("can't interate over ComplementaryIntervalSet") 395 | 396 | def count (self, low, high): 397 | iterator = iter (self.intervals) 398 | c = 0 399 | prev = low 400 | try: 401 | i = next (iterator) 402 | while i[1] < low: 403 | i = next (iterator) 404 | while i[0] < high: 405 | c += i[0] - prev 406 | prev = i[1] + 1 407 | i = next (iterator) 408 | except StopIteration: 409 | pass 410 | if prev < high: 411 | c += high - prev 412 | return c 413 | 414 | def min (self): 415 | if not self.intervals or self.intervals[0][0] > 0: 416 | return 0 417 | else: 418 | return self.intervals[0][1] + 1 419 | 420 | def max (self): 421 | raise RuntimeError ('the maximum of a ComplementaryIntervalSet is infinity') 422 | 423 | def intersection (self, other): 424 | if isinstance (other, ComplementaryIntervalSet): 425 | return ~(~self).union (~other) 426 | else: 427 | return IntervalSet.intersection (self, other) 428 | 429 | def union (self, other): 430 | return ~(~self).intersection (~other) 431 | 432 | def _to_xml (self): 433 | if not self.intervals: 434 | return E ('N') 435 | else: 436 | return E ('apply', E ('complement'), IntervalSet._to_xml (self)) 437 | 438 | 439 | N = ComplementaryIntervalSet ([]) 440 | 441 | CSAObject.tag_map[CSA + 'N'] = (N, SINGLETON) 442 | -------------------------------------------------------------------------------- /csa/misc.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | from . import _misc 20 | 21 | def disc (r): 22 | return _misc.Disc (r) 23 | 24 | def rectangle (width, height): 25 | return _misc.Rectangle (width, height) 26 | 27 | def gaussian (sigma, cutoff): 28 | return _misc.Gaussian (sigma, cutoff) 29 | 30 | def block (M, N = None): 31 | return _misc.Block (M, M if N == None else N) 32 | 33 | def repeat (M, N = None): 34 | return _misc.Repeat (M, M if N == None else N) 35 | 36 | transpose = _misc.Transpose () 37 | 38 | def shift (M, N): 39 | return _misc.Shift (M, N) 40 | 41 | fix = _misc.Fix () 42 | 43 | def block1 (N): 44 | return _misc.Block (N) 45 | 46 | #del _misc # not for export 47 | -------------------------------------------------------------------------------- /csa/plot.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012,2022 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | import numpy as _numpy 20 | import matplotlib 21 | import matplotlib.pyplot as _plt 22 | 23 | from . import elementary 24 | 25 | # This function was autogenerated by boilerplate.py. Do not edit as 26 | # changes will be lost 27 | def inverseGray(): 28 | ''' 29 | set the default colormap to gray and apply to current image if any. 30 | See help(colormaps) for more information 31 | ''' 32 | _plt.rc('image', cmap='gray_r') 33 | im = _plt.gci() 34 | 35 | if im is not None: 36 | im.set_cmap(_plt.cm.gray_r) 37 | _plt.draw_if_interactive() 38 | 39 | def show (cset, N0 = 30, N1 = None): 40 | N1 = N0 if N1 == None else N1 41 | _plt.clf () 42 | _plt.axis ('equal') 43 | a = _numpy.zeros ((N0, N1)) 44 | for (i, j) in elementary.cross (range (N0), range (N1)) * cset: 45 | a[i,j] += 1.0 46 | _plt.imshow (a, interpolation='nearest', vmin = 0.0, vmax = 1.0) 47 | _plt.show () 48 | 49 | def gplotsel2d (g, cset, source = elementary.N, target = elementary.N, N0 = 900, N1 = None, value = None, range=[], lines = True): 50 | N1 = N0 if N1 == None else N1 51 | _plt.clf () 52 | _plt.axis ('equal') 53 | gplot2d (g, N1, color = 'grey', show = False) 54 | cset = elementary.cross (source, target) * cset 55 | N = len (cset) 56 | if lines: 57 | marker = 'ro-' 58 | else: 59 | marker = 'ro' 60 | if elementary.arity (cset): 61 | if value != None: 62 | if range: 63 | normalize = matplotlib.colors.Normalize (*range) 64 | else: 65 | normalize = matplotlib.colors.Normalize () 66 | normalize.autoscale ([v[value] for (i, j, v) in cset.c]) 67 | cmap = matplotlib.cm.get_cmap () 68 | for (i, j, v) in cset.c: 69 | color = cmap (normalize (v[value])) 70 | _plt.plot ([g (i)[0], g (j)[0]], [g (i)[1], g (j)[1]], \ 71 | marker, color = color, mfc = color) 72 | else: 73 | for (i, j, v) in cset.c: 74 | _plt.plot ([g (i)[0], g (j)[0]], [g (i)[1], g (j)[1]], marker) 75 | else: 76 | for (i, j) in cset: 77 | _plt.plot ([g (i)[0], g (j)[0]], [g (i)[1], g (j)[1]], marker) 78 | _plt.show () 79 | 80 | def gplot2d (g, N, color = None, show = True): 81 | if show: 82 | _plt.clf () 83 | _plt.axis ('equal') 84 | x = [] 85 | y = [] 86 | for i in range (0, N): 87 | pos = g (i) 88 | x.append (pos[0]) 89 | y.append (pos[1]) 90 | if color != None: 91 | _plt.plot (x, y, 'o', color = color) 92 | else: 93 | _plt.plot (x, y, 'bo') 94 | if show: 95 | _plt.show () 96 | 97 | #del numpy, plt 98 | -------------------------------------------------------------------------------- /csa/valueset.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | from .csaobject import * 20 | 21 | class ValueSet (CSAObject): 22 | def __init__ (self): 23 | CSAObject.__init__ (self, "valueset") 24 | 25 | def __neg__ (self): 26 | return GenericValueSet (lambda i, j: - self (i, j)) 27 | 28 | def __add__ (self, other): 29 | if not callable (other): 30 | return maybeAffine (other, 1.0, self) 31 | elif isinstance (other, (QuotedValueSet, AffineValueSet)): 32 | return other.__add__ (self) 33 | elif isinstance (other, GenericValueSet): 34 | return GenericValueSet (lambda i, j: self (i, j) + other.function (i, j)) 35 | else: 36 | return GenericValueSet (lambda i, j: self (i, j) + other (i, j)) 37 | 38 | def __radd__ (self, other): 39 | return self.__add__ (other) 40 | 41 | def __sub__ (self, other): 42 | return self.__add__ (- other) 43 | 44 | def __rsub__ (self, other): 45 | return self.__neg__ ().__add__ (other) 46 | 47 | def __mul__ (self, other): 48 | if not callable (other): 49 | return maybeAffine (0.0, other, self) 50 | elif isinstance (other, (QuotedValueSet, AffineValueSet)): 51 | return other.__mul__ (self) 52 | elif isinstance (other, GenericValueSet): 53 | return GenericValueSet (lambda i, j: self (i, j) * other.function (i, j)) 54 | else: 55 | return GenericValueSet (lambda i, j: self (i, j) * other (i, j)) 56 | 57 | def __rmul__ (self, other): 58 | return self.__mul__ (other) 59 | 60 | 61 | class QuotedValueSet (ValueSet): 62 | def __init__ (self, expression): 63 | ValueSet.__init__ (self) 64 | self.expression = expression 65 | 66 | def __call__ (self, i, j): 67 | return self.expression 68 | 69 | def __neg__ (self): 70 | return QuotedValueSet (- self.expression) 71 | 72 | def __add__ (self, other): 73 | if not callable (other): 74 | return QuotedValueSet (self.expression + other) 75 | elif isinstance (other, QuotedValueSet): 76 | return QuotedValueSet (self.expression + other.expression) 77 | elif isinstance (other, AffineValueSet): 78 | return other.__add__ (self) 79 | else: 80 | return maybeAffine (self.expression, 1.0, other) 81 | 82 | def __mul__ (self, other): 83 | if not callable (other): 84 | return QuotedValueSet (self.expression * other) 85 | elif isinstance (other, QuotedValueSet): 86 | return QuotedValueSet (self.expression * other.expression) 87 | elif isinstance (other, AffineValueSet): 88 | return other.__mul__ (self) 89 | else: 90 | return maybeAffine (0.0, self.expression, other) 91 | 92 | 93 | class GenericValueSet (ValueSet): 94 | def __init__ (self, function): 95 | ValueSet.__init__ (self) 96 | self.function = function 97 | 98 | def __call__ (self, i, j): 99 | return self.function (i, j) 100 | 101 | def __neg__ (self): 102 | return GenericValueSet (lambda i, j: - self.function (i, j)) 103 | 104 | def __add__ (self, other): 105 | if not callable (other): 106 | return maybeAffine (other, 1.0, self.function) 107 | elif isinstance (other, (QuotedValueSet, AffineValueSet)): 108 | return other.__add__ (self) 109 | elif isinstance (other, GenericValueSet): 110 | return GenericValueSet (lambda i, j: self.function (i, j) + other.function (i, j)) 111 | else: 112 | return GenericValueSet (lambda i, j: self.function (i, j) + other (i, j)) 113 | 114 | def __mul__ (self, other): 115 | if not callable (other): 116 | return maybeAffine (0.0, other, self.function) 117 | elif isinstance (other, (QuotedValueSet, AffineValueSet)): 118 | return other.__mul__ (self) 119 | elif isinstance (other, GenericValueSet): 120 | return GenericValueSet (lambda i, j: self.function (i, j) * other.function (i, j)) 121 | else: 122 | return GenericValueSet (lambda i, j: self.function (i, j) * other (i, j)) 123 | 124 | 125 | class AffineValueSet (ValueSet): 126 | def __init__ (self, constant, coefficient, function): 127 | ValueSet.__init__ (self) 128 | self.const = constant 129 | self.coeff = coefficient 130 | self.func = function 131 | 132 | def __call__ (self, i, j): 133 | return self.const + self.coeff * self.func (i, j) 134 | 135 | def __neg__ (self): 136 | return maybeAffine (- self.const, - self.coeff, self.func) 137 | 138 | def __add__ (self, other): 139 | if not callable (other): 140 | return maybeAffine (self.const + other, self.coeff, self.func) 141 | elif isinstance (other, QuotedValueSet): 142 | return maybeAffine (self.const + other.expression, 143 | self.coeff, self.func) 144 | elif isinstance (other, AffineValueSet): 145 | f = lambda i, j: \ 146 | self.const * self.func (i, j) \ 147 | + other.const * other.func (i, j) 148 | return maybeAffine (self.const + other.const, 149 | 1.0, 150 | f) 151 | 152 | def __mul__ (self, other): 153 | if not callable (other): 154 | return maybeAffine (self.const * other, 155 | self.coeff * other, 156 | self.func) 157 | elif isinstance (other, QuotedValueSet): 158 | return maybeAffine (self.const * other.expression, 159 | self.coeff * other.expression, 160 | self.func) 161 | elif isinstance (other, AffineValueSet): 162 | f = lambda i, j: \ 163 | other.const * self.coeff * self.func (i, j) \ 164 | + self.const * other.coeff * other.func (i, j) \ 165 | + self.coeff * other.coeff \ 166 | * self.func (i, j) * other.func (i, j) 167 | return maybeAffine (self.const * other.const, 168 | 1.0, 169 | f) 170 | 171 | def maybeAffine (const, coeff, func): 172 | if coeff == 0.0: 173 | return QuotedValueSet (const) 174 | elif const == 0.0 and coeff == 1.0: 175 | return GenericValueSet (func) 176 | else: 177 | return AffineValueSet (const, coeff, func) 178 | -------------------------------------------------------------------------------- /csa/version.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012,2018,2020 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | __version__ = "0.1.12" 20 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | python-csa (0.1.12-1) unstable; urgency=medium 2 | 3 | * New upstream version 4 | 5 | -- Mikael Djurfeldt Tue, 07 Apr 2020 11:58:54 +0200 6 | 7 | python-csa (0.1.10-1) unstable; urgency=medium 8 | 9 | * New upstream version 10 | 11 | -- Mikael Djurfeldt Wed, 22 Jan 2020 21:23:37 +0100 12 | 13 | python-csa (0.1.0-1) unstable; urgency=low 14 | 15 | * New upstream version (Closes: #597299) 16 | 17 | -- Mikael Djurfeldt Fri, 30 Mar 2012 15:32:17 +0200 18 | 19 | python-csa (0.0.4-1) unstable; urgency=high 20 | 21 | * New upstream version (Closes: #590343) 22 | 23 | -- Mikael Djurfeldt Wed, 28 Jul 2010 01:19:48 +0200 24 | 25 | python-csa (0.0.3-2) unstable; urgency=high 26 | 27 | * Added missing depends (Closes: #590343) 28 | 29 | -- Mikael Djurfeldt Tue, 27 Jul 2010 16:54:20 +0200 30 | 31 | python-csa (0.0.3-1) unstable; urgency=high 32 | 33 | * Added shift operator (+ make sure previous bug fixes goes in quickly 34 | to testing). 35 | 36 | -- Mikael Djurfeldt Tue, 27 Jul 2010 07:58:54 +0200 37 | 38 | python-csa (0.0.2-1) unstable; urgency=low 39 | 40 | * Bug fixes and extensions. 41 | 42 | -- Mikael Djurfeldt Sat, 17 Jul 2010 11:30:38 +0200 43 | 44 | python-csa (0.0.1-1) unstable; urgency=low 45 | 46 | * Initial release (Closes: #588360) 47 | 48 | -- Mikael Djurfeldt Wed, 07 Jul 2010 17:45:19 +0200 49 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: python-csa 2 | Section: python 3 | Priority: optional 4 | Maintainer: Mikael Djurfeldt 5 | Build-Depends: debhelper (>= 10) 6 | , dh-python 7 | , python3-setuptools 8 | , python3-all 9 | , python3-nose 10 | , python3-numpy 11 | , python3-matplotlib 12 | Standards-Version: 4.4.1 13 | Homepage: https://github.com/INCF/csa 14 | 15 | Package: python3-csa 16 | Architecture: any 17 | Depends: ${python3:Depends}, ${misc:Depends}, python3-numpy, python3-matplotlib 18 | Description: Connection-Set Algebra (CSA) implemented in Python 19 | The CSA library provides elementary connection-sets and operators for 20 | combining them. It also provides an iteration interface to such 21 | connection-sets enabling efficient iteration over existing connections 22 | with a small memory footprint also for very large networks. The CSA 23 | can be used as a component of neuronal network simulators or other 24 | tools. 25 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This work was packaged for Debian by: 2 | 3 | Mikael Djurfeldt on Wed, 7 Jul 2010 16:55:01 +0100 4 | 5 | Upstream Author: Mikael Djurfeldt 6 | 7 | Copyright: Copyright © 2010 Mikael Djurfeldt 8 | 9 | License: GPL-3 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with this program. If not, see . 23 | 24 | On Debian systems, the complete text of the GNU General 25 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 26 | 27 | The Debian packaging is: 28 | 29 | Copyright (C) 2010 Mikael Djurfeldt 30 | 31 | and is licensed under the GPL version 3, see above. 32 | -------------------------------------------------------------------------------- /debian/python-csa.docs: -------------------------------------------------------------------------------- 1 | README 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | override_dh_auto_test: 4 | ifeq ($(filter nocheck,$(DEB_BUILD_OPTIONS)),) 5 | set -e; \ 6 | for python in $(shell pyversions -r); do \ 7 | $$python /usr/bin/nosetests ../../tests/test_csa.py; \ 8 | done 9 | endif 10 | 11 | %: 12 | dh $@ --with python3 --buildsystem=pybuild 13 | 14 | #override_dh_auto_configure: 15 | # dh_auto_configure -- --enable-deb --with-python=python3 16 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /doc/ChangeLog: -------------------------------------------------------------------------------- 1 | 2012-03-30 Mikael Djurfeldt 2 | 3 | * Release 0.1.0 4 | 5 | * tutorial.tex: Version 0.2. 6 | 7 | 2011-01-18 Mikael Djurfeldt 8 | 9 | * tutorial.tex: New file: Version 0.1. 10 | 11 | -------------------------------------------------------------------------------- /doc/figures/blockRandom.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/blockRandom.pdf -------------------------------------------------------------------------------- /doc/figures/brro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/brro.pdf -------------------------------------------------------------------------------- /doc/figures/cartesian.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/cartesian.pdf -------------------------------------------------------------------------------- /doc/figures/disc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/disc.pdf -------------------------------------------------------------------------------- /doc/figures/gaussnet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/gaussnet.pdf -------------------------------------------------------------------------------- /doc/figures/grid2d.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/grid2d.pdf -------------------------------------------------------------------------------- /doc/figures/intersection.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/intersection.pdf -------------------------------------------------------------------------------- /doc/figures/oneToOne.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/oneToOne.pdf -------------------------------------------------------------------------------- /doc/figures/projection.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/projection.pdf -------------------------------------------------------------------------------- /doc/figures/random.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/random.pdf -------------------------------------------------------------------------------- /doc/figures/random2d.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/random2d.pdf -------------------------------------------------------------------------------- /doc/figures/setDifference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/setDifference.pdf -------------------------------------------------------------------------------- /doc/figures/twoPoints.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/twoPoints.pdf -------------------------------------------------------------------------------- /doc/figures/visualDisc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INCF/csa/006a56e5e5acffe8e19a78f08f5571af71932748/doc/figures/visualDisc.pdf -------------------------------------------------------------------------------- /doc/manual/basicfunc.rst: -------------------------------------------------------------------------------- 1 | Basic functions 2 | =============== 3 | 4 | Connection-set constructor 5 | -------------------------- 6 | 7 | .. function:: cset (mask, valueSet, ...) 8 | 9 | Selectors 10 | --------- 11 | 12 | .. function:: mask (object) 13 | 14 | .. function:: value (object, k) 15 | 16 | .. function:: arity (object) 17 | 18 | Cartesian product 19 | ----------------- 20 | 21 | .. function:: cross (set1, set2) 22 | 23 | Elementary masks 24 | ---------------- 25 | 26 | 27 | 28 | Support for parallel simulators 29 | ------------------------------- 30 | 31 | .. function:: partition (cset, masks, selected[, seed]) 32 | 33 | Utilities 34 | --------- 35 | 36 | .. function:: tabulate (cset) 37 | -------------------------------------------------------------------------------- /doc/manual/datatypes.rst: -------------------------------------------------------------------------------- 1 | Datatypes in the Python CSA implementation 2 | ========================================== 3 | 4 | Connection Set Objects 5 | ---------------------- 6 | 7 | Connection-set objects (:class:`Mask`, :class:`ConnectionSet`) 8 | 9 | +--------------------+---------------------------------+--------+ 10 | | Operation | Result | Notes | 11 | +====================+=================================+========+ 12 | | ``x * y`` | intersection of *x* and | | 13 | | | *y* | | 14 | +--------------------+---------------------------------+--------+ 15 | | ``x - y`` | set difference of *x* and *y* | | 16 | | | | | 17 | +--------------------+---------------------------------+--------+ 18 | 19 | Test: 20 | 21 | +-------------+---------------------------------+-------+ 22 | | Operation | Result | Notes | 23 | +=============+=================================+=======+ 24 | | ``x or y`` | if *x* is false, then *y*, else | \(1) | 25 | | | *x* | | 26 | +-------------+---------------------------------+-------+ 27 | | ``x and y`` | if *x* is false, then *x*, else | \(2) | 28 | | | *y* | | 29 | +-------------+---------------------------------+-------+ 30 | | ``not x`` | if *x* is false, then ``True``, | \(3) | 31 | | | else ``False`` | | 32 | +-------------+---------------------------------+-------+ 33 | 34 | 35 | Value Set Objects 36 | ----------------- 37 | 38 | Interval Set Objects 39 | -------------------- 40 | 41 | -------------------------------------------------------------------------------- /doc/manual/geometry.rst: -------------------------------------------------------------------------------- 1 | Geometry 2 | ======== 3 | -------------------------------------------------------------------------------- /doc/manual/index.rst: -------------------------------------------------------------------------------- 1 | The Python CSA Reference Manual 2 | =============================== 3 | Contents: 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | intro 9 | datatypes 10 | basicfunc 11 | random 12 | geometry 13 | tutorial 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /doc/manual/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Getting started 5 | --------------- 6 | 7 | Installing CSA 8 | -------------- 9 | -------------------------------------------------------------------------------- /doc/manual/random.rst: -------------------------------------------------------------------------------- 1 | Random connectivity 2 | =================== 3 | -------------------------------------------------------------------------------- /doc/manual/tutorial.rst: -------------------------------------------------------------------------------- 1 | A CSA tutorial 2 | ============== 3 | -------------------------------------------------------------------------------- /doc/tutorial.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper,twoside]{report} 2 | 3 | \usepackage{listings} 4 | \usepackage{color} 5 | \usepackage{graphicx} 6 | \usepackage{makeidx} 7 | 8 | % Use the head environment around method heads 9 | \lstnewenvironment{head}[1]% 10 | {\lstset{frame=topline,emph={#1},emphstyle=\color{blue}\textbf}}% 11 | {} 12 | 13 | % Use the parameters environment after heads 14 | \newenvironment{parameters}% 15 | {\begin{tabular}{@{\hspace{2em}}lp{0.6\textwidth}}}% 16 | {\end{tabular}\par\vspace{1mm}\par\hrule\par\vspace{5mm}} 17 | 18 | % Use the code environment around method code examples 19 | \lstnewenvironment{code}[1]% 20 | {\lstset{frame=single,caption={#1}}}% 21 | {} 22 | 23 | \renewcommand{\lstlistingname}{Example} 24 | 25 | %\lstset{language=python,basicstyle=\ttfamily\small} 26 | \lstset{language=python,identifierstyle=\ttfamily} 27 | 28 | \newcommand{\cls}[1]{\lstinline|#1|} 29 | \newcommand{\fa}[1]{\lstinline|#1|} 30 | \newcommand{\expr}[1]{\lstinline|#1|} 31 | \newcommand{\ret}{\emph{return value}} 32 | \newcommand{\self}{\emph{self}} 33 | 34 | \title{Python-csa tutorial v0.2} 35 | \author{Mikael Djurfeldt} 36 | \date{2011-01-17} 37 | 38 | \makeindex 39 | 40 | \begin{document} 41 | 42 | \maketitle 43 | 44 | \tableofcontents 45 | 46 | \chapter{Purpose of this document} 47 | This is a preliminary documentation and tutorial for the python-csa 48 | demonstration implementation in Python of the Connection-Set Algebra 49 | (Djurfeldt, 2011, submitted) 50 | 51 | The CSA library provides elementary connection-sets and operators for 52 | combining them. It also provides an iteration interface to such 53 | connection-sets enabling efficient iteration over existing connections 54 | with a small memory footprint also for very large networks. The CSA 55 | can be used as a component of neuronal network simulators or other 56 | tools. 57 | 58 | Section \ref{sec:introduction} introduces some basic concepts while 59 | section \ref{sec:tutorial} provides some \emph{hands-on} material for 60 | getting started. Section \ref{sec:reference} contain a preliminary 61 | reference documentation. 62 | 63 | \chapter{Introduction}\label{sec:introduction} 64 | When building a neuronal network model, we often want to connect one 65 | set of neurons---the \emph{source} set---with another set---the 66 | \emph{target} set. When applying the Connection-Set Algebra 67 | (hereafter denoted \emph{CSA}), we start by \emph{enumerating} the 68 | source and target sets, i.e. we assign arbitrary integer indices to 69 | the neurons of each set. This allows us to represent a connection 70 | between source neuron number 3 and target neuron number 17 as a pair 71 | of integers (3, 17). More generally, the source and target sets do 72 | not need to be neurons. For example, the target set might be a set of 73 | synaptic sites. Also, source and target sets can be (and is often) 74 | the same set. This is the case when using CSA to describe 75 | connectivity within a neuronal population. 76 | 77 | \begin{itemize} 78 | \item A \emph{mask} contains information about which connections exist. It 79 | is a set of (source, target) pairs, one pair for each existing 80 | connection. It can also be regarded as a function mapping a pair of 81 | arbitrary non-negative integers to a boolean value---\emph{true} for 82 | each existing connection. 83 | \item A \emph{value-set} is a function mapping each existing 84 | connection to a value, such as a synaptic weight. 85 | \item A \emph{connection-set} is a tuple of a mask and zero or more 86 | value sets. 87 | \end{itemize} 88 | 89 | CSA connection sets are usually infinite. This is a simplification 90 | compared to the common situation of finite source and target sets in 91 | that the sizes of these sets do not need to be considered. Connection 92 | sets can have arbitrary values associated with connections. Pure 93 | connection sets without any values associated are called masks. 94 | 95 | \chapter{Tutorial}\label{sec:tutorial} 96 | 97 | \section{Basic concepts} 98 | 99 | To get access to the CSA in Python, type: 100 | 101 | \begin{code}{} 102 | from csa import * 103 | \end{code} 104 | 105 | The mask representing all possible connections between an infinite 106 | source and target set is: 107 | 108 | \begin{code}{} 109 | full 110 | \end{code} 111 | 112 | To display a finite portion of the corresponding connectivity matrix, 113 | type: 114 | 115 | \begin{code}{} 116 | show (full) 117 | \end{code} 118 | 119 | One-to-one connectivity (where source node 0 is connected to target 120 | node 0, source 1 to target 1 etc) is represented by the mask oneToOne (Figure \ref{fig:oneToOne}): 121 | 122 | \begin{code}{} 123 | show (oneToOne) 124 | \end{code} 125 | 126 | \begin{figure} 127 | \begin{center} 128 | \includegraphics[width=0.5\textwidth]{figures/oneToOne} 129 | \caption[oneToOne mask]{\label{fig:oneToOne} 130 | \expr{oneToOne} 131 | } 132 | \end{center} 133 | \end{figure} 134 | \pagebreak 135 | The default portion displayed by "show" is (0, 29) x (0, 29). 136 | (0, 99) x (0, 99) can be displayed using: 137 | 138 | \begin{code}{} 139 | show (oneToOne, 100, 100) 140 | \end{code} 141 | 142 | If source and target set is the same, oneToOne describes 143 | self-connections. We can use CSA to compute the set of connections 144 | consisting of all possible connections except for self-connections 145 | using the set difference operator "-" (Figure \ref{fig:setDifference}): 146 | 147 | \begin{code}{} 148 | show (full - oneToOne) 149 | \end{code} 150 | 151 | \begin{figure} 152 | \begin{center} 153 | \includegraphics[width=0.5\textwidth]{figures/setDifference} 154 | \caption[Set difference]{\label{fig:setDifference} 155 | \expr{full - oneToOne} 156 | } 157 | \end{center} 158 | \end{figure} 159 | 160 | Finite connection sets can be represented using either lists of 161 | connections, with connections represented as tuples (Figure 162 | \ref{fig:twoPoints}): 163 | 164 | \begin{code}{} 165 | show ([(22, 7), (8, 23)]) 166 | \end{code} 167 | 168 | \begin{figure} 169 | \begin{center} 170 | \includegraphics[width=0.5\textwidth]{figures/twoPoints} 171 | \caption[Mask with two connections]{\label{fig:twoPoints} 172 | \expr{[(22, 7), (8, 23)]} 173 | } 174 | \end{center} 175 | \end{figure} 176 | 177 | or using the Cartesian product of intervals (Figure \ref{fig:cartesian}): 178 | 179 | \begin{code}{} 180 | show (cross (xrange (10), xrange (20))) 181 | \end{code} 182 | 183 | \begin{figure} 184 | \begin{center} 185 | \includegraphics[width=0.5\textwidth]{figures/cartesian} 186 | \caption[Cartesian mask]{\label{fig:cartesian} 187 | \expr{xrange (10), xrange (20)} 188 | } 189 | \end{center} 190 | \end{figure} 191 | \pagebreak 192 | We can form a finite version of the infinite oneToOne by taking the 193 | intersection "*" with a finite connection set (Figure \ref{fig:intersection}): 194 | 195 | \begin{code}{} 196 | c = cross (xrange (10), xrange (10)) * oneToOne 197 | show (c) 198 | \end{code} 199 | 200 | \begin{figure} 201 | \begin{center} 202 | \includegraphics[width=0.5\textwidth]{figures/intersection} 203 | \caption[Finite part of infinite set]{\label{fig:intersection} 204 | \expr{cross (xrange (10), xrange (10)) * oneToOne} 205 | } 206 | \end{center} 207 | \end{figure} 208 | 209 | Finite connection sets can be tabulated: 210 | 211 | \begin{code}{} 212 | >>> tabulate(c) 213 | 0 0 214 | 1 1 215 | 2 2 216 | 3 3 217 | 4 4 218 | 5 5 219 | 6 6 220 | 7 7 221 | 8 8 222 | 9 9 223 | \end{code} 224 | \pagebreak 225 | In Python, finite connection sets provide an iterator interface: 226 | 227 | \begin{code}{} 228 | >>> for x in cross (xrange (4), xrange (4)) * oneToOne: 229 | ... print x 230 | ... 231 | (0, 0) 232 | (1, 1) 233 | (2, 2) 234 | (3, 3) 235 | \end{code} 236 | 237 | \section{Random connectivity} 238 | 239 | Connectivity where the existence of each possible connection is 240 | determined by a Bernoulli trial with probability p is expressed with 241 | the random mask random (p), e.g. (Figure \ref{fig:random}): 242 | 243 | \begin{code}{} 244 | show (random (0.5)) 245 | \end{code} 246 | 247 | \begin{figure} 248 | \begin{center} 249 | \includegraphics[width=0.5\textwidth]{figures/random} 250 | \caption[Random mask]{\label{fig:random} 251 | \expr{random (0.5)} 252 | } 253 | \end{center} 254 | \end{figure} 255 | 256 | \section{The block operator} 257 | The block operator expands each connection in the operand into a 258 | rectangular block in the resulting connection matrix, e.g. (Figure \ref{fig:blockRandom}): 259 | 260 | \begin{code}{} 261 | show (block (5,3) * random (0.5)) 262 | \end{code} 263 | 264 | \begin{figure} 265 | \begin{center} 266 | \includegraphics[width=0.5\textwidth]{figures/blockRandom} 267 | \caption[Block expanded random mask]{\label{fig:blockRandom} 268 | \expr{block (5,3) * random (0.5)} 269 | } 270 | \end{center} 271 | \end{figure} 272 | 273 | Note that "*" here means operator application (see section 274 | \ref{sec:opap}). There is also a quadratic version of the operator: 275 | 276 | \begin{code}{} 277 | show (block (10) * random (0.7)) 278 | \end{code} 279 | 280 | Using intersection and set difference, we can now formulate a more 281 | complex mask: 282 | 283 | \begin{code}{} 284 | show (block (10) * random (0.7) * random (0.5) - oneToOne) 285 | \end{code} 286 | 287 | \begin{figure} 288 | \begin{center} 289 | \includegraphics[width=0.5\textwidth]{figures/brro} 290 | \caption[Random mask]{\label{fig:brro} 291 | \expr{block (10) * random (0.7) * random (0.5) - oneToOne} 292 | } 293 | \end{center} 294 | \end{figure} 295 | 296 | The block operator is especially useful when creating connectivity 297 | with hierarchical substructure, such as a set of cortical columns. 298 | 299 | \section{Geometry} 300 | 301 | In CSA, the basic tool to handle distance dependent connectivity is 302 | metrics. Metrics are value sets d (i, j). Metrics can be defined 303 | through geometry functions. A geometry function maps an index to a 304 | position. We can, for example, assign a random position in the unit 305 | square to each index: 306 | 307 | \begin{code}{} 308 | g = random2d (900) 309 | \end{code} 310 | 311 | The positions of the grid described by g have indices from 0 to 899 312 | and can be displayed like this: 313 | 314 | \begin{code}{} 315 | gplot2d (g, 900) 316 | \end{code} 317 | 318 | \begin{figure} 319 | \begin{center} 320 | \includegraphics[width=0.5\textwidth]{figures/random2d} 321 | \caption[Random geometry]{\label{fig:random2d} 322 | \expr{gplot2d (random2d (900), 900)} 323 | } 324 | \end{center} 325 | \end{figure} 326 | 327 | Alternatively, we can arrange indices in a 30 x 30 grid within the 328 | unit square: 329 | 330 | \begin{code}{} 331 | g = grid2d (30) 332 | \end{code} 333 | 334 | \begin{figure} 335 | \begin{center} 336 | \includegraphics[width=0.5\textwidth]{figures/grid2d} 337 | \caption[Random geometry]{\label{fig:grid2d} 338 | \expr{gplot2d (grid2d (30), 900)} 339 | } 340 | \end{center} 341 | \end{figure} 342 | 343 | We can now define the euclidean metric on this grid: 344 | 345 | \begin{code}{} 346 | d = euclidMetric2d (g) 347 | \end{code} 348 | 349 | An example of a distance dependent connection set is the disc mask 350 | Disc (r) * d which connects each index i to all indices j within a 351 | distance d (i, j) < r: 352 | 353 | \begin{code}{} 354 | c = disc (r) * d 355 | \end{code} 356 | 357 | To examine the result we can employ the function gplotsel2d (g, c, i) 358 | which displays the targets g (j) of i in the connection set c (Figure 359 | \ref{fig:disc}): 360 | 361 | \begin{code}{} 362 | gplotsel2d (g, c, 434) 363 | \end{code} 364 | \noindent [A known bug in the current implementation makes the above 365 | expression crash. This only happens for infinite sets like \expr{c} 366 | and can be amended by intersecting it with a finite set: \expr{cross 367 | (xrange (900), xrange (900)) * c}.] 368 | 369 | \begin{figure} 370 | \begin{center} 371 | \includegraphics[width=0.5\textwidth]{figures/disc} 372 | \caption[Disc geometry]{\label{fig:disc} 373 | Projection from source neuron \#434 in \expr{disc (0.3) * d}. 374 | } 375 | \end{center} 376 | \end{figure} 377 | 378 | In the case where the connection set represents a projection between 379 | two different coordinate systems, we define one geometry function for 380 | each. In the following example \expr{g1} is direction in visual space 381 | in arc minutes while \expr{g2} is position in the cortical 382 | representation of the Macaque fovea in mm: 383 | 384 | \begin{code}{} 385 | g1 = grid2d (30) 386 | g2 = grid2d (30, x0 = -7.0, xScale = 8.0, yScale = 8.0) 387 | \end{code} 388 | 389 | We now define a projection operator which takes visual coordinates 390 | into cortical (Dow et al. 1985): 391 | 392 | \begin{code}{} 393 | import cmath 394 | 395 | @ProjectionOperator 396 | def GvspaceToCx (p): 397 | w = 7.7 * cmath.log (complex (p[0] + 0.33, p[1])) 398 | return (w.real, w.imag) 399 | \end{code} 400 | 401 | To see how the grid g1 is transformed into cortical space, we type: 402 | 403 | \begin{code}{} 404 | gplot2d (GvspaceToCx * g1, 900) 405 | \end{code} 406 | 407 | \begin{figure} 408 | \begin{center} 409 | \includegraphics[width=0.5\textwidth]{figures/projection} 410 | \caption[Logarithmic projection]{\label{fig:projection} 411 | \expr{gplot2d (GvspaceToCx * g1, 900)} 412 | } 413 | \end{center} 414 | \end{figure} 415 | 416 | The inverse projection is defined: 417 | 418 | \begin{code}{} 419 | @ProjectionOperator 420 | def GcxToVspace (p): 421 | c = cmath.exp (complex (p[0], p[1]) / 7.7) - 0.33 422 | return (c.real, c.imag) 423 | \end{code} 424 | 425 | Real receptive field sizes vary with eccentricity. Assume, for now, 426 | that we want to connect each target index to sources within a disc of 427 | constant radius. We then need to project back into visual space and 428 | use the disc operator: 429 | 430 | \begin{code}{} 431 | c = disc (0.1) * euclidMetric2d (g1, GcxToVspace * g2) 432 | \end{code} 433 | 434 | Again, we use gplotsel2d to check the result (Figure \ref{fig:visualDisc}): 435 | 436 | \begin{code}{} 437 | gplotsel2d (g2, c, 282) 438 | \end{code} 439 | 440 | \begin{figure} 441 | \begin{center} 442 | \includegraphics[width=0.5\textwidth]{figures/visualDisc} 443 | \caption[Random geometry]{\label{fig:visualDisc} 444 | \expr{disc (0.1) * euclidMetric2d (g1, GcxToVspace * g2)} 445 | } 446 | \end{center} 447 | \end{figure} 448 | \clearpage 449 | \pagebreak 450 | \section{A network with gaussian connectivity} 451 | In the following example we represent the connectivity of a network 452 | with excitatory and inhibitory neurons and gaussian connectivity in a 453 | random geometry using a single connection-set (Figure \ref{fig:gaussnet}). 454 | 455 | \begin{code}{Network with gaussian geometry-dependent connectivity} 456 | from csa import * 457 | 458 | # Create index intervals for excitatory, inhibitory 459 | # and all cells 460 | e = ival (0, 599) 461 | i = ival (600, 899) 462 | a = e + i 463 | 464 | # Create geometry function g and metric d 465 | g = random2d (900) 466 | d = euclidMetric2d (g) 467 | 468 | # Excitatory and inhibitory conductances, computed as 469 | # gaussian value sets (provides the gaussian of the 470 | # distance for every index pair) 471 | g_e = gaussian (0.1, 0.3) * d 472 | g_i = gaussian (0.2, 0.3) * d 473 | 474 | # Create connection-sets with gaussian dependent random 475 | # masks, gaussian dependent conductance and distance 476 | # dependent delay: (mask, conductance, delay) 477 | c_e = cset (random * g_e, g_e, d) 478 | c_i = cset (random * g_i, -g_i, d) 479 | 480 | # Combine excitatory and inhibitory connectivity into one 481 | # network using intersection (*) and multiset sum (+) 482 | # operators 483 | c = cross (e, a) * c_e + cross (i, a) * c_i 484 | 485 | # We may also plot the outgoing connections from one 486 | # excitatory neuron around coordinate (0.33, 0.5) and one 487 | # inhibitory neuron around coordinate (0.67, 0.5) 488 | sources = [g.inverse(0.33,0.5,e), g.inverse(0.67,0.5,i)] 489 | gplotsel2d (g, c, sources, value=0, range=[-1,1]) 490 | \end{code} 491 | 492 | \begin{figure} 493 | \begin{center} 494 | \includegraphics[width=0.9\textwidth]{figures/gaussnet} 495 | \caption[Projections of an excitatory and an inhibitory neuron]{\label{fig:gaussnet} 496 | Projections of an excitatory (warm colors) and an inhibitory 497 | (cold colors). 498 | } 499 | \end{center} 500 | \end{figure} 501 | 502 | \chapter{Reference}\label{sec:reference} 503 | 504 | %\begin{head}{} 505 | %\end{head} 506 | %\begin{parameters} 507 | % \lstinline|| &% 508 | % \\ 509 | % \lstinline|| &% 510 | % \\ 511 | % \ret &% 512 | % \\ 513 | %\end{parameters} 514 | 515 | This section documents how to use existing python-csa classes. 516 | 517 | \section{Classes} 518 | This section briefly documents some important classes in the 519 | python-csa implementation and their public API. The examples use many 520 | elements which are defined in later sections. It is suggested to use 521 | the index on page \pageref{sec:index} to find the reference 522 | documentation for these elements. 523 | 524 | \subsection{ConnectionSet}\index{ConnectionSet} 525 | A connection-set can be regarded as a set of connections, represented 526 | by their source and target indices, with zero or more associated 527 | values. In the CSA, a connection-set with no associated values is a 528 | mask. Thus, in the python-csa implementation, in all cases where an 529 | instance of the class \cls{ConnectionSet} is expected, it is OK to 530 | pass an instance of \cls{Mask}. 531 | 532 | \begin{head}{__len__} 533 | __len__ (self) 534 | \end{head} 535 | \begin{parameters} 536 | \ret &% 537 | the number of connections in this connection-set\\ 538 | \end{parameters} 539 | This method returns the number of connections in this connection-set. 540 | An error is reported if this connection-set isn't finite. 541 | \begin{code}{Obtaining the number of connections in a connection-set} 542 | >>> len (cross ((0, 1), (0, 1))) 543 | 4 544 | \end{code} 545 | 546 | \begin{head}{__iter__} 547 | __iter__ (self) 548 | \end{head} 549 | \begin{parameters} 550 | \ret &% 551 | iterator over the connections represented by this instance\\ 552 | \end{parameters} 553 | This method returns an iterator over the connections represented by 554 | this instance. Each item generated by the iterator is a tuple 555 | \[ (i, j, v_0, ..., v_{n-1}) \] 556 | \begin{code}{Iterating over a connection-set} 557 | >>> m = cross ((0, 1), (2, 3)) 558 | >>> v = vset (lambda i, j: i + j) 559 | >>> c = cset (m, v, v * v) 560 | >>> for x in c: 561 | ... print x 562 | ... 563 | (0, 2, 2, 4) 564 | (1, 2, 3, 9) 565 | (0, 3, 3, 9) 566 | (1, 3, 4, 16) 567 | \end{code} 568 | 569 | \subsection{Mask}\index{Mask} 570 | A mask gives information about which connections exist. It can be 571 | regarded as a set of connections, represented by their source and 572 | target indices. In the CSA, a connection-set with no associated 573 | values is a mask. In the python-csa implementation, an attempt to 574 | construct a connection-set with zero associated values, yields an 575 | instance of the class \cls{Mask}. In cases where a mask is expected, 576 | a python list of (source, target) tuples can also be passed. 577 | 578 | The class \cls{Mask} has the same public methods (\expr{__len__}, 579 | \expr{__iter__}) as the class \cls{ConnectionSet}. 580 | 581 | \subsection{ValueSet}\index{ValueSet} 582 | To be documented. 583 | 584 | \subsection{IntervalSet}\index{IntervalSet}\label{sec:intervalset} 585 | To be documented. 586 | 587 | \section{Constructor and selectors} 588 | 589 | \subsection{cset}\index{cset} 590 | 591 | \begin{head}{cset} 592 | cset (mask, valueSet, ...) 593 | \end{head} 594 | \begin{parameters} 595 | \lstinline|mask| &% 596 | a \lstinline|Mask| \\ 597 | \lstinline|valueSet| &% 598 | zero or more \lstinline|ValueSet|:s \\ 599 | \end{parameters} 600 | This function constructs and returns a connection-set from a 601 | \lstinline|Mask| and zero or more \lstinline|ValueSet|:s. [Note: In 602 | the current implementation, \lstinline|mask| is returned if no 603 | value-sets are given. This should probably change so that a new 604 | object is returned.] 605 | 606 | \subsection{mask}\index{mask} 607 | 608 | \begin{head}{mask} 609 | mask (cset) 610 | \end{head} 611 | \begin{parameters} 612 | \lstinline|cset| &% 613 | a \lstinline|ConnectionSet|\\ 614 | \emph{return value} &% 615 | the \lstinline|Mask| of \lstinline|cset|\\ 616 | \end{parameters} 617 | This function returns the \lstinline|Mask| of the 618 | \lstinline|ConnectionSet| \lstinline|cset|. 619 | 620 | \subsection{value}\index{value} 621 | 622 | \begin{head}{value} 623 | value (cset, k) 624 | \end{head} 625 | \begin{parameters} 626 | \lstinline|cset| &% 627 | a \lstinline|ConnectionSet|\\ 628 | \lstinline|k| &% 629 | index of the value-set to return\\ 630 | \emph{return value} &% 631 | the \lstinline|k|:th \lstinline|ValueSet| of \lstinline|cset|\\ 632 | \end{parameters} 633 | This function returns the \lstinline|k|:th \lstinline|ValueSet| of the 634 | \lstinline|ConnectionSet| \lstinline|cset|. 635 | 636 | \subsection{arity}\index{arity} 637 | 638 | \begin{head}{arity} 639 | arity (cset) 640 | \end{head} 641 | \begin{parameters} 642 | \lstinline|cset| &% 643 | a \lstinline|ConnectionSet|\\ 644 | \emph{return value} &% 645 | the \emph{arity} of \lstinline|cset|\\ 646 | \end{parameters} 647 | This function returns the \emph{arity} of the 648 | \lstinline|ConnectionSet| \lstinline|cset|. The arity of a 649 | connection-set is the number of value-sets of the connection-set. 650 | 651 | \subsection{vset}\index{vset} 652 | 653 | \begin{head}{vset} 654 | vset (x) 655 | vset (callable) 656 | \end{head} 657 | \begin{parameters} 658 | \lstinline|x| &% 659 | a value\\ 660 | \lstinline|callable| &% 661 | a callable taking two arguments\\ 662 | \end{parameters} 663 | This function constructs and returns a value-set. In the first form, 664 | the number \fa{x} is taken as the value of each of all existing 665 | connections. In the second form, the value of each existing 666 | connection is the one returned by applying \fa{callable} to the source 667 | and target indices of the connection. 668 | 669 | \section{Integer sets} 670 | In the current python-csa implementation, integer sets are usually 671 | represented using the class \cls{IntervalSet} (see section 672 | \ref{sec:intervalset}). Functions that take integer sets as arguments 673 | generally coerce \cls{tuple}:s of two non-negative integers into 674 | \cls{IntervalSet}:s: 675 | \begin{code}{} 676 | (1, 2) --> IntervalSet ([(1,2)]) 677 | \end{code} 678 | 679 | \subsection{ival}\index{ival} 680 | 681 | \begin{head}{ival} 682 | ival (beginning, end) 683 | \end{head} 684 | \begin{parameters} 685 | \lstinline|beginning| &% 686 | start of interval\\ 687 | \lstinline|end| &% 688 | end of interval (inclusive)\\ 689 | \emph{return value} &% 690 | the interval \lstinline|(beginning, end)|\\ 691 | \end{parameters} 692 | This function returns the interval \lstinline|(beginning, end)| 693 | represented as a set of non-negative integers. The underlying 694 | representation is space-efficient. 695 | 696 | \subsection{N}\index{N} 697 | 698 | \begin{head}{N} 699 | N 700 | \end{head} 701 | \begin{parameters} 702 | \end{parameters} 703 | This constant represents the set of all non-negative integers. 704 | 705 | \subsection{cross}\index{cross} 706 | 707 | \begin{head}{cross} 708 | cross (set0, set1) 709 | \end{head} 710 | \begin{parameters} 711 | \lstinline|set0| &% 712 | a set of non-negative integers\\ 713 | \lstinline|set1| &% 714 | a set of non-negative integers\\ 715 | \emph{return value} &% 716 | the Cartesian cross product of \fa{set0} and \fa{set1}\\ 717 | \end{parameters} 718 | This function returns the Cartesian cross product of \fa{set0} and 719 | \fa{set1} represented as a \cls{Mask}. 720 | 721 | \begin{code}{The Cartesian product of (1,2) and (3,4)} 722 | >>> tabulate (cross ((1,2), (3,4))) 723 | 1 3 724 | 2 3 725 | 1 4 726 | 2 4 727 | \end{code} 728 | 729 | \section{Utilities} 730 | 731 | \begin{head}{tabulate} 732 | tabulate (cset) 733 | \end{head} 734 | \begin{parameters} 735 | \lstinline|cset| &% 736 | a \cls{ConnectionSet}\\ 737 | \end{parameters} 738 | This procedure tabulates the connection-set \fa{cset}. An iteration 739 | over the connections in \fa{cset} is performed. The source and target 740 | indices are tabulated in the first and second columns with value-sets 741 | tabulated in columns three and upwards. 742 | 743 | Tabulate can be used to print connection-sets during development. 744 | 745 | \section{Elementary masks} 746 | 747 | \subsection{empty}\index{empty} 748 | 749 | \begin{head}{empty} 750 | empty 751 | \end{head} 752 | \begin{parameters} 753 | \end{parameters} 754 | This constant \cls{Mask} represents the set of no connection. 755 | Iterating results in nothing, no matter how hard you try. 756 | 757 | \subsection{full}\index{full} 758 | 759 | \begin{head}{full} 760 | full 761 | \end{head} 762 | \begin{parameters} 763 | \end{parameters} 764 | This constant \cls{Mask} represents the (infinite) set of all 765 | connections. 766 | 767 | \begin{code}{Finite portion of the \expr{full} mask} 768 | >>> tabulate (cross ((0, 1), (0, 1)) * full) 769 | 0 0 770 | 1 0 771 | 0 1 772 | 1 1 773 | \end{code} 774 | 775 | \subsection{oneToOne}\index{oneToOne} 776 | \begin{head}{oneToOne} 777 | oneToOne 778 | \end{head} 779 | \begin{parameters} 780 | \end{parameters} 781 | This constant \cls{Mask} represents the (infinite) set of one-to-one 782 | connections. It resembles Kronecker's delta or an infinite identity 783 | matrix. 784 | 785 | \begin{code}{Finite portion of the \expr{oneToOne} mask} 786 | >>> tabulate (cross ((0, 3), (0, 3)) * oneToOne) 787 | 0 0 788 | 1 1 789 | 2 2 790 | 3 3 791 | \end{code} 792 | 793 | \subsection{random}\index{random} 794 | \begin{head}{random} 795 | random (p) 796 | \end{head} 797 | \begin{parameters} 798 | \lstinline|p| &% 799 | the probability for a potential connection to exist\\ 800 | \ret &% 801 | an infinite \cls{Mask} where the existence of each connection is 802 | determined by a Bernoulli trial with probability \fa{p}.\\ 803 | \end{parameters} 804 | This function returns a random mask where a connection between given 805 | source and target indices exists with probability \fa{p}. 806 | 807 | See also section \ref{sec:randomop} for the set of functions returning 808 | random \emph{operators}. These support sampling a given number of 809 | connections from a finite mask or random sampling with constraints on 810 | \fa{fanIn} or \fa{fanOut}. 811 | 812 | \section{Set operators} 813 | The following binary operators can be applied to integer sets, 814 | masks and connection-sets: 815 | \par\vspace{4mm}\hrule\par\vspace{1mm} 816 | \begin{tabular}{@{\hspace{2em}}lp{0.6\textwidth}} 817 | \expr{A + B} & the \emph{multiset sum} of A and B\\ 818 | \expr{A - B} & the \emph{set difference} between A and B\\ 819 | \expr{A * B} & the \emph{intersection} of A and B\\ 820 | \end{tabular}\par\vspace{1mm}\par\hrule\par\vspace{5mm} 821 | 822 | In addition, the following unary operator applies to integer sets and masks: 823 | \par\vspace{4mm}\hrule\par\vspace{1mm} 824 | \begin{tabular}{@{\hspace{2em}}lp{0.6\textwidth}} 825 | \expr{\~A} & the \emph{complement} of A\\ 826 | \end{tabular}\par\vspace{1mm}\par\hrule\par\vspace{5mm} 827 | 828 | \section{Arithmetic operators} 829 | The arithmetic operators on connection-sets which are defined in the 830 | connection-set algebra are not yet implemented in the python-csa demo 831 | implementation. 832 | 833 | \section{Operator application}\label{sec:opap} 834 | The operator application operator is used to apply unary 835 | connection-set algebra operators to their operand: 836 | \par\vspace{4mm}\hrule\par\vspace{1mm} 837 | \begin{tabular}{@{\hspace{2em}}lp{0.6\textwidth}} 838 | \expr{operator * operand} & apply \fa{operator} to \fa{operand}\\ 839 | \end{tabular}\par\vspace{1mm}\par\hrule\par\vspace{5mm} 840 | The operator application operator is overloaded with the arithmetic 841 | multiplication and set intersection operators. 842 | 843 | \section{Miscellaneous connection-set operators}\label{sec:miscop} 844 | 845 | \subsection{random}\index{random}\label{sec:randomop} 846 | \begin{head}{random} 847 | random (N = n) * cset 848 | \end{head} 849 | \begin{parameters} 850 | \lstinline|n| &% 851 | the number of connections to sample (keyword arg named \fa{N})\\ 852 | \fa{cset} &% 853 | any \emph{finite} connection-set\\ 854 | \ret &% 855 | a connection-set containing \fa{n} randomly sampled connections from 856 | \fa{cset}\\ 857 | \end{parameters} 858 | 859 | \begin{head}{random} 860 | random (fanIn = n) * cset 861 | \end{head} 862 | \begin{parameters} 863 | \lstinline|n| &% 864 | the number of sources sampled for each target (keyword arg named \fa{fanIn})\\ 865 | \fa{cset} &% 866 | any \emph{finite} connection-set\\ 867 | \ret &% 868 | a connection-set randomly sampled from \fa{cset} with fanIn \fa{n}\\ 869 | \end{parameters} 870 | 871 | \begin{head}{random} 872 | random (fanOut = n) * cset 873 | \end{head} 874 | \begin{parameters} 875 | \lstinline|n| &% 876 | the number of targets sampled for each source (keyword arg named \fa{fanOut})\\ 877 | \fa{cset} &% 878 | any \emph{finite} connection-set\\ 879 | \ret &% 880 | a connection-set randomly sampled from \fa{cset} with fanOut \fa{n}\\ 881 | \end{parameters} 882 | 883 | \subsection{disc}\index{disc} 884 | 885 | \begin{head}{disc} 886 | disc (r) * metric 887 | \end{head} 888 | \begin{parameters} 889 | \fa{r} & radius \\ 890 | \ret & a mask of all connections for which 891 | \expr{metric (source, target) < r} \\ 892 | \end{parameters} 893 | 894 | \subsection{gaussian}\index{gaussian} 895 | 896 | \begin{head}{gaussian} 897 | gaussian (sigma, cutoff) * metric 898 | \end{head} 899 | \begin{parameters} 900 | \lstinline|sigma| &% 901 | \\ 902 | \lstinline|cutoff| &% 903 | \\ 904 | \ret & a value set associating the result of applying the normalized 905 | gaussian function with standard deviation \fa{sigma} and cutoff 906 | \fa{cutoff} to \expr{metric (source, target)} to each connection\\ 907 | \end{parameters} 908 | 909 | \subsection{block}\index{block} 910 | 911 | \begin{head}{block} 912 | block (M, N) 913 | block (M) 914 | \end{head} 915 | \begin{parameters} 916 | \fa{M} & \\ 917 | \fa{N} & \\ 918 | \end{parameters} 919 | 920 | \subsection{block1}\index{block1} 921 | 922 | \begin{head}{block1} 923 | block1 (N) 924 | \end{head} 925 | \begin{parameters} 926 | \end{parameters} 927 | 928 | \subsection{transpose}\index{transpose} 929 | 930 | \begin{head}{} 931 | transpose 932 | \end{head} 933 | \begin{parameters} 934 | \end{parameters} 935 | 936 | \subsection{shift}\index{shift} 937 | 938 | \begin{head}{shift} 939 | shift (M, N) 940 | \end{head} 941 | \begin{parameters} 942 | \lstinline|M| &% 943 | \\ 944 | \lstinline|N| &% 945 | \\ 946 | \end{parameters} 947 | 948 | \subsection{fix}\index{fix} 949 | 950 | \begin{head}{fix} 951 | fix 952 | \end{head} 953 | \begin{parameters} 954 | \end{parameters} 955 | 956 | \section{Geometry} 957 | 958 | \subsection{grid2d}\index{grid2d} 959 | 960 | \begin{head}{grid2d} 961 | grid2d (width, xScale = 1.0, yScale = 1.0, x0 = 0.0, y0 = 0.0) 962 | \end{head} 963 | \begin{parameters} 964 | \lstinline|width| &% 965 | \\ 966 | \lstinline|xScale| &% 967 | \\ 968 | \emph{return value} &% 969 | \\ 970 | \end{parameters} 971 | 972 | \subsection{random2d}\index{random2d} 973 | 974 | \begin{head}{random2d} 975 | random2d (N, xScale = 1.0, yScale = 1.0) 976 | \end{head} 977 | \begin{parameters} 978 | \lstinline|| &% 979 | \\ 980 | \lstinline|| &% 981 | \\ 982 | \emph{return value} &% 983 | \\ 984 | \end{parameters} 985 | 986 | \subsection{euclidMetric2d}\index{euclidMetric2d} 987 | 988 | \begin{head}{} 989 | euclidMetric2d (g1, [g2]) 990 | \end{head} 991 | \begin{parameters} 992 | \lstinline|g1| &% 993 | \\ 994 | \lstinline|g2| optional &% 995 | \\ 996 | \emph{return value} &% 997 | \\ 998 | \end{parameters} 999 | 1000 | \subsection{ProjectionOperator}\index{ProjectionOperator} 1001 | 1002 | \begin{head}{ProjectionOperator} 1003 | @ProjectionOperator 1004 | def fname (p): 1005 | ... 1006 | return q 1007 | \end{head} 1008 | \begin{parameters} 1009 | \lstinline|fname| &% 1010 | \\ 1011 | \lstinline|p| &% 1012 | \\ 1013 | \end{parameters} 1014 | 1015 | \section{Plotting} 1016 | 1017 | \subsection{show}\index{show} 1018 | 1019 | \begin{head}{show} 1020 | show (cset, N0 = 30, [N1]) 1021 | \end{head} 1022 | \begin{parameters} 1023 | \lstinline|cset| &% 1024 | \\ 1025 | \lstinline|N0| &% 1026 | \\ 1027 | \end{parameters} 1028 | 1029 | \subsection{gplotsel2d}\index{gplotsel2d} 1030 | 1031 | \begin{head}{gplotsel2d} 1032 | gplotsel2d (g, cset, source = N, target = N, 1033 | N0 = 900, [N1], [value], range=[], lines = True) 1034 | \end{head} 1035 | \begin{parameters} 1036 | \lstinline|| &% 1037 | \\ 1038 | \lstinline|| &% 1039 | \\ 1040 | \end{parameters} 1041 | 1042 | \begin{head}{gplot2d} 1043 | gplot2d (g, N, [color], show = True) 1044 | \end{head} 1045 | \begin{parameters} 1046 | \lstinline|| &% 1047 | \\ 1048 | \lstinline|| &% 1049 | \\ 1050 | \end{parameters} 1051 | 1052 | \label{sec:index} 1053 | \printindex 1054 | 1055 | \end{document} 1056 | 1057 | 1058 | %%% Local Variables: 1059 | %%% mode: latex 1060 | %%% TeX-master: t 1061 | %%% eval: (flyspell-mode 1) 1062 | %%% eval: (ispell-change-dictionary "american") 1063 | %%% eval: (flyspell-buffer) 1064 | %%% End: 1065 | -------------------------------------------------------------------------------- /examples/catLGNV1.py: -------------------------------------------------------------------------------- 1 | from csa import * 2 | 3 | # Dow et al. 1985 (Macaque fovea) 4 | @ProjectionOperator 5 | def GvspaceToCx (p): 6 | w = 7.7 * cmath.log (complex (p[0] + 0.33, p[1])) 7 | return (w.real, w.imag) 8 | 9 | @ProjectionOperator 10 | def GcxToVspace (p): 11 | c = cmath.exp (complex (p[0], p[1]) / 7.7) - 0.33 12 | return (c.real, c.imag) 13 | 14 | # g1 = grid2d (30) 15 | # g2 = grid2d (30, x0 = -7.0, xScale = 8.0, yScale = 8.0) 16 | # c = disc (0.1) * euclidMetric2d (g1, GcxToVspace * g2) 17 | # gplotsel2d (g2, c, 282) 18 | -------------------------------------------------------------------------------- /libpycsa/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with Automake to create Makefile.in 2 | 3 | ACLOCAL = $(top_srcdir)/aclocal.sh 4 | 5 | lib_LTLIBRARIES = libpycsa.la 6 | 7 | libpycsa_la_SOURCES = pycsa.h pycsa.cpp 8 | 9 | libpycsa_la_HEADERS = pycsa.h 10 | libpycsa_la_CPPFLAGS = @LIBPYCSA_CPPFLAGS@ @LIBNEUROSIM_INCLUDE@ 11 | libpycsa_la_CXXFLAGS = @LIBPYCSA_CXXFLAGS@ 12 | libpycsa_la_LIBADD = @LIBNEUROSIM_PY_LIBS@ @LIBNEUROSIM_LIBS@ @PYTHONLIB@ 13 | libpycsa_la_LDFLAGS = \ 14 | -version-info 1:0:0 -export-dynamic -Wl,-z,defs 15 | libpycsa_ladir = $(includedir) 16 | 17 | MKDEP = gcc -M $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) 18 | -------------------------------------------------------------------------------- /libpycsa/pycsa.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * pycsa.cpp 3 | * 4 | * This file is part of libneurosim. 5 | * 6 | * Copyright (C) 2013 INCF and 2018, 2020 Mikael Djurfeldt 7 | * 8 | * libneurosim is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * libneurosim is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | #include "pycsa.h" 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #if PY_MAJOR_VERSION >= 3 31 | #define PYINT_ASLONG PyLong_AsLong 32 | #define PYINT_FROMLONG PyLong_FromLong 33 | #else 34 | #define PYINT_ASLONG PyInt_AsLong 35 | #define PYINT_FROMLONG PyInt_FromLong 36 | #endif 37 | 38 | static PyObject* pMask = 0; 39 | static PyObject* pConnectionSet = 0; 40 | static PyObject* pCSAClasses = 0; 41 | static PyObject* pArity = 0; 42 | static PyObject* pCross = 0; 43 | static PyObject* pPartition = 0; 44 | static PyObject* pParse = 0; 45 | static PyObject* pParseString = 0; 46 | 47 | 48 | static void 49 | error (std::string errstring) 50 | { 51 | PYGILSTATE_ENSURE (gstate); 52 | PyErr_SetString (PyExc_RuntimeError, errstring.c_str ()); 53 | PYGILSTATE_RELEASE (gstate); 54 | } 55 | 56 | 57 | static bool 58 | CSAimported () 59 | { 60 | if (!Py_IsInitialized ()) 61 | Py_Initialize (); 62 | return PyMapping_HasKeyString (PyImport_GetModuleDict (), (char*)"csa"); 63 | } 64 | 65 | 66 | static bool 67 | loadCSA () 68 | { 69 | PYGILSTATE_ENSURE (gstate); 70 | PyObject* pModule = PyMapping_GetItemString (PyImport_GetModuleDict (), (char*)"csa"); 71 | 72 | pMask = PyObject_GetAttrString (pModule, "Mask"); 73 | if (pMask == NULL) 74 | { 75 | Py_DECREF (pModule); 76 | PYGILSTATE_RELEASE (gstate); 77 | error ("Couldn't find the Mask class in the CSA library"); 78 | return false; 79 | } 80 | 81 | pConnectionSet = PyObject_GetAttrString (pModule, "ConnectionSet"); 82 | if (pConnectionSet == NULL) 83 | { 84 | Py_DECREF (pModule); 85 | PYGILSTATE_RELEASE (gstate); 86 | error ("Couldn't find the ConnectionSet class in the CSA library"); 87 | return false; 88 | } 89 | 90 | pArity = PyObject_GetAttrString (pModule, "arity"); 91 | pCross = PyObject_GetAttrString (pModule, "cross"); 92 | pPartition = PyObject_GetAttrString (pModule, "partition"); 93 | 94 | pParse = PyObject_GetAttrString (pModule, "parse"); 95 | pParseString = PyObject_GetAttrString (pModule, "parseString"); 96 | 97 | Py_DECREF (pModule); 98 | if (pArity == NULL) 99 | { 100 | PYGILSTATE_RELEASE (gstate); 101 | error ("Couldn't find the arity function in the CSA library"); 102 | return false; 103 | } 104 | 105 | pCSAClasses = PyTuple_Pack (2, pMask, pConnectionSet); 106 | PYGILSTATE_RELEASE (gstate); 107 | return true; 108 | } 109 | 110 | 111 | static bool 112 | tryLoadCSA () 113 | { 114 | if (pCSAClasses == 0) 115 | { 116 | if (!CSAimported ()) 117 | PyRun_SimpleString ("import csa\n"); //*fixme* error handling 118 | 119 | // load CSA library 120 | bool status = loadCSA (); 121 | if (!status) 122 | return false; 123 | } 124 | } 125 | 126 | 127 | namespace PyCSA { 128 | 129 | PyCSAGenerator::PyCSAGenerator (PyObject* obj) 130 | : pCSAObject (obj), pPartitionedCSAObject (NULL), pIterator (NULL) 131 | { 132 | PYGILSTATE_ENSURE (gstate); 133 | Py_INCREF (pCSAObject); 134 | PyObject* a = PyObject_CallFunctionObjArgs (pArity, pCSAObject, NULL); 135 | arity_ = PYINT_ASLONG (a); 136 | Py_DECREF (a); 137 | PYGILSTATE_RELEASE (gstate); 138 | } 139 | 140 | 141 | PyCSAGenerator::~PyCSAGenerator () 142 | { 143 | PYGILSTATE_ENSURE (gstate); 144 | Py_XDECREF (pIterator); 145 | Py_XDECREF (pPartitionedCSAObject); 146 | Py_DECREF (pCSAObject); 147 | PYGILSTATE_RELEASE (gstate); 148 | } 149 | 150 | 151 | int 152 | PyCSAGenerator::arity () 153 | { 154 | return arity_; 155 | } 156 | 157 | 158 | PyObject* 159 | PyCSAGenerator::makeIntervals (IntervalSet& iset) 160 | { 161 | PyObject* ivals = PyList_New (0); 162 | if (iset.skip () == 1) 163 | { 164 | for (IntervalSet::iterator i = iset.begin (); i != iset.end (); ++i) 165 | PyList_Append (ivals, 166 | PyTuple_Pack (2, 167 | PYINT_FROMLONG (i->first), 168 | PYINT_FROMLONG (i->last))); 169 | } 170 | else 171 | { 172 | for (IntervalSet::iterator i = iset.begin (); i != iset.end (); ++i) 173 | { 174 | int last = i->last; 175 | for (int j = i->first; j < last; j += iset.skip ()) 176 | PyList_Append (ivals, 177 | PyTuple_Pack (2, 178 | PYINT_FROMLONG (j), 179 | PYINT_FROMLONG (j))); 180 | } 181 | } 182 | return ivals; 183 | } 184 | 185 | 186 | void 187 | PyCSAGenerator::setMask (std::vector& masks, int local) 188 | { 189 | PYGILSTATE_ENSURE (gstate); 190 | PyObject* pMasks = PyList_New (masks.size ()); 191 | for (size_t i = 0; i < masks.size (); ++i) 192 | { 193 | PyObject* pMask 194 | = PyObject_CallFunctionObjArgs (pCross, 195 | makeIntervals (masks[i].sources), 196 | makeIntervals (masks[i].targets), 197 | NULL); 198 | PyList_SetItem (pMasks, i, pMask); 199 | } 200 | 201 | Py_XDECREF (pPartitionedCSAObject); 202 | pPartitionedCSAObject = PyObject_CallFunctionObjArgs (pPartition, 203 | pCSAObject, 204 | pMasks, 205 | PYINT_FROMLONG (local), 206 | NULL); 207 | if (pPartitionedCSAObject == NULL) 208 | { 209 | PYGILSTATE_RELEASE (gstate); 210 | std::cerr << "Failed to create masked CSA object" << std::endl; 211 | return; 212 | } 213 | Py_INCREF (pPartitionedCSAObject); //*fixme* check if necessary! 214 | PYGILSTATE_RELEASE (gstate); 215 | } 216 | 217 | 218 | int 219 | PyCSAGenerator::size () 220 | { 221 | PYGILSTATE_ENSURE (gstate); 222 | int size = PySequence_Size (pCSAObject); 223 | PYGILSTATE_RELEASE (gstate); 224 | return size; 225 | } 226 | 227 | 228 | void 229 | PyCSAGenerator::start () 230 | { 231 | if (pPartitionedCSAObject == NULL) 232 | { 233 | error ("CSA connection generator not properly initialized"); 234 | return; 235 | } 236 | PYGILSTATE_ENSURE (gstate); 237 | Py_XDECREF (pIterator); 238 | pIterator = PyObject_GetIter (pPartitionedCSAObject); 239 | PYGILSTATE_RELEASE (gstate); 240 | } 241 | 242 | 243 | bool 244 | PyCSAGenerator::next (int& source, int& target, double* value) 245 | { 246 | if (pIterator == NULL) 247 | { 248 | error ("Must call start() before next()"); 249 | return false; 250 | } 251 | 252 | PYGILSTATE_ENSURE (gstate); 253 | PyObject* tuple = PyIter_Next (pIterator); 254 | PyObject* err = PyErr_Occurred (); 255 | if (err) 256 | { 257 | PYGILSTATE_RELEASE (gstate); 258 | return false; 259 | } 260 | 261 | if (tuple == NULL) 262 | { 263 | Py_DECREF (pIterator); 264 | pIterator = NULL; 265 | PYGILSTATE_RELEASE (gstate); 266 | return false; 267 | } 268 | 269 | source = PYINT_ASLONG (PyTuple_GET_ITEM (tuple, 0)); 270 | target = PYINT_ASLONG (PyTuple_GET_ITEM (tuple, 1)); 271 | for (int i = 0; i < arity_; ++i) 272 | { 273 | PyObject* v = PyTuple_GET_ITEM (tuple, i + 2); 274 | if (!PyFloat_Check (v)) 275 | { 276 | Py_DECREF (tuple); 277 | PYGILSTATE_RELEASE (gstate); 278 | error ("NEST cannot handle non-float CSA value sets"); 279 | return false; 280 | } 281 | value[i] = PyFloat_AsDouble (v); 282 | } 283 | 284 | Py_DECREF (tuple); 285 | PYGILSTATE_RELEASE (gstate); 286 | return true; 287 | } 288 | 289 | static bool 290 | isPyCSAGenerator (PyObject* obj) 291 | { 292 | if (pCSAClasses == 0) 293 | { 294 | if (!CSAimported ()) 295 | return false; 296 | 297 | // load CSA library 298 | bool status = loadCSA (); 299 | if (!status) 300 | return false; 301 | } 302 | 303 | return PyObject_IsInstance (obj, pCSAClasses); 304 | } 305 | 306 | 307 | static ConnectionGenerator* 308 | unpackPyCSAGenerator (PyObject* pObj) 309 | { 310 | if (isPyCSAGenerator (pObj)) 311 | return new PyCSAGenerator (pObj); 312 | else 313 | return 0; 314 | } 315 | 316 | static ConnectionGenerator* 317 | parseString (std::string xml) 318 | { 319 | if (!tryLoadCSA ()) 320 | return 0; 321 | PYGILSTATE_ENSURE (gstate); 322 | PyObject* pyXML = PyUnicode_FromString (xml.c_str ()); 323 | PyObject* cg = PyObject_CallFunctionObjArgs (pParseString, pyXML, NULL); 324 | Py_DECREF (pyXML); 325 | PYGILSTATE_RELEASE (gstate); 326 | return new PyCSAGenerator (cg); 327 | } 328 | 329 | static ConnectionGenerator* 330 | parseFile (std::string fname) 331 | { 332 | if (!tryLoadCSA ()) 333 | return 0; 334 | PYGILSTATE_ENSURE (gstate); 335 | PyObject* pyfname = PyUnicode_FromString (fname.c_str ()); 336 | PyObject* cg = PyObject_CallFunctionObjArgs (pParseString, pyfname, NULL); 337 | Py_DECREF (pyfname); 338 | PYGILSTATE_RELEASE (gstate); 339 | return new PyCSAGenerator (cg); 340 | } 341 | 342 | // Publicly visible in PyCSA namespace 343 | void 344 | init () 345 | { 346 | registerConnectionGeneratorLibrary ("libpycsa.so", 347 | parseString, 348 | parseFile, 349 | 0, 350 | 0); 351 | PNS::registerConnectionGeneratorType (isPyCSAGenerator, 352 | unpackPyCSAGenerator); 353 | } 354 | 355 | } 356 | 357 | namespace { 358 | struct initializer { 359 | initializer() { 360 | PyCSA::init (); 361 | } 362 | }; 363 | static initializer i; 364 | } 365 | -------------------------------------------------------------------------------- /libpycsa/pycsa.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pycsa.h 3 | * 4 | * This file is part of libneurosim. 5 | * 6 | * Copyright (C) 2013 INCF 7 | * 8 | * libneurosim is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * libneurosim is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | #ifndef PYCSA_H 24 | #define PYCSA_H 25 | 26 | extern "C" { 27 | #include 28 | } 29 | 30 | #ifdef WITH_THREAD 31 | /* CPython compiled with threads */ 32 | #define PYGILSTATE_ENSURE(VAR) PyGILState_STATE VAR = PyGILState_Ensure() 33 | #define PYGILSTATE_RELEASE(VAR) PyGILState_Release (VAR) 34 | #else 35 | #define PYGILSTATE_ENSURE(VAR) 36 | #define PYGILSTATE_RELEASE(VAR) 37 | #endif 38 | 39 | #include 40 | 41 | namespace PyCSA { 42 | 43 | class PyCSAGenerator : public ConnectionGenerator { 44 | PyObject* pCSAObject; 45 | PyObject* pPartitionedCSAObject; 46 | int arity_; 47 | PyObject* pIterator; 48 | private: 49 | PyObject* makeIntervals (IntervalSet& iset); 50 | public: 51 | PyCSAGenerator (PyObject* obj); 52 | 53 | ~PyCSAGenerator (); 54 | 55 | int arity (); 56 | 57 | void setMask (std::vector& masks, int local); 58 | 59 | int size (); 60 | 61 | void start (); 62 | 63 | bool next (int& source, int& target, double* value); 64 | }; 65 | 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | # ugly hack to prevent matplotlib from creating a configuration file 6 | # outside of the EasyInstall sandbox 7 | import os 8 | os.environ['MPLCONFIGDIR'] = "." 9 | 10 | # read version without actually importing the module 11 | import ast 12 | __version__ = ast.parse (open ("csa/version.py").read ()).body[0].value.s 13 | 14 | long_description = """The CSA library provides elementary connection-sets and operators for 15 | combining them. It also provides an iteration interface to such 16 | connection-sets enabling efficient iteration over existing connections 17 | with a small memory footprint also for very large networks. The CSA 18 | can be used as a component of neuronal network simulators or other 19 | tools.""" 20 | 21 | setup ( 22 | name = "csa", 23 | version = __version__, 24 | packages = ['csa',], 25 | author = "Mikael Djurfeldt", # add your name here if you contribute to the code 26 | author_email = "mikael@djurfeldt.com", 27 | description = "The Connection-Set Algebra implemented in Python", 28 | long_description = long_description, 29 | license = "GPLv3", 30 | keywords = "computational neuroscience modeling connectivity", 31 | url = "http://software.incf.org/software/csa/", 32 | classifiers = ['Development Status :: 3 - Alpha', 33 | 'Environment :: Console', 34 | 'Intended Audience :: Science/Research', 35 | 'License :: OSI Approved :: GNU General Public License (GPL)', 36 | 'Natural Language :: English', 37 | 'Operating System :: OS Independent', 38 | 'Programming Language :: Python :: 2.7', 39 | 'Programming Language :: Python :: 3.4', 40 | 'Programming Language :: Python :: 3.5', 41 | 'Programming Language :: Python :: 3.6', 42 | 'Topic :: Scientific/Engineering'], 43 | install_requires = ['numpy', 'matplotlib'], 44 | ) 45 | -------------------------------------------------------------------------------- /tests/test_csa.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of the Connection-Set Algebra (CSA). 3 | # Copyright (C) 2010,2011,2012 Mikael Djurfeldt 4 | # 5 | # CSA is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # CSA is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | import sys 20 | import numpy 21 | 22 | from csa import * 23 | 24 | import unittest 25 | 26 | import sys 27 | if sys.version < '3.4': 28 | from contextlib import contextmanager 29 | @contextmanager 30 | def redirect_stdout(stream): 31 | old_stdout = sys.stdout 32 | sys.stdout = stream 33 | try: 34 | yield 35 | finally: 36 | sys.stdout = old_stdout 37 | else: 38 | from contextlib import redirect_stdout 39 | from io import StringIO 40 | 41 | 42 | class TestCSA(unittest.TestCase): 43 | def assertEqualCS (self, cs, ls, msg): 44 | self.assertEqual ([x for x in cs], ls, msg) 45 | 46 | def assertEqual4x4 (self, cs, ls, msg): 47 | self.assertEqualCS (cross ((0, 3), (0, 3)) * cs, ls, msg) 48 | 49 | def assertEqual30x30 (self, cs, ls, msg): 50 | self.assertEqualCS (cross ((0, 29), (0, 29)) * cs, ls, msg) 51 | 52 | def sampleN (self, func, dims, N): 53 | data = numpy.zeros ((N,) + dims) 54 | for k in range (N): 55 | data[k] = func () 56 | return numpy.mean (data, 0) 57 | 58 | 59 | class TestElementary (TestCSA): 60 | 61 | # Only use setUp() and tearDown() if necessary 62 | 63 | #def setUp(self): 64 | # ... code to execute in preparation for tests ... 65 | 66 | #def tearDown(self): 67 | # ... code to execute to clean up after tests ... 68 | def test_list (self): 69 | # Test list cs 70 | self.assertEqual30x30 ([(22,7),(8,23)], 71 | [(22,7),(8,23)], 72 | 'list cs') 73 | 74 | def test_range (self): 75 | # Test range 76 | self.assertEqual30x30 (cross (range (10), range (10, 20, 3)), 77 | [(i, j) for j in range (10, 20, 3) for i in range (10)], 78 | 'xrange specified interval set mask') 79 | 80 | def test_full (self): 81 | # Test full cs 82 | self.assertEqualCS (cross ((0, 7), (8, 15)) * full, 83 | [(i, j) for j in range (8, 16) for i in range (0, 8)], 84 | 'finite piece of full connection-set bogus') 85 | 86 | def test_oneToOne (self): 87 | # Test oneToOne cs 88 | self.assertEqualCS (cross ((0, 7), (1, 8)) * oneToOne, 89 | [(i, i) for i in range (1, 8)], 90 | 'finite piece of oneToOne connection-set bogus') 91 | 92 | def test_tabulate (self): 93 | # Test tabulate 94 | if sys.version < '2.6': #*fixme* 95 | return 96 | s = StringIO() 97 | with redirect_stdout(s): 98 | tabulate (cross ((0, 3), (0, 3)) * oneToOne) 99 | self.assertEqual (s.getvalue (), 100 | '0 \t0\n1 \t1\n2 \t2\n3 \t3\n', 101 | 'tabulate malfunctioning') 102 | 103 | 104 | def test_gaussnet (self): 105 | e = ival (0, 19) 106 | i = ival (20, 29) 107 | a = e + i 108 | 109 | g = random2d (900) 110 | d = euclidMetric2d (g) 111 | 112 | g_e = gaussian (0.1, 0.3) * d 113 | g_i = gaussian (0.2, 0.3) * d 114 | 115 | c_e = cset (random * g_e, g_e) 116 | c_i = cset (random * g_i, -g_i) 117 | 118 | c = cross (e, a) * c_e + cross (i, a) * c_i 119 | 120 | self.assertTrue (cross (N, 0) * c) 121 | self.assertFalse (cross (N, 100) * c) 122 | 123 | for (i, j, g) in cross (i, a) * c: 124 | self.assertTrue (g < 0.0) 125 | 126 | 127 | def partitionRandomN (self): 128 | K = self.K 129 | N = 3 * K 130 | R = (0, N - 1) 131 | R0 = (0, K - 1) 132 | R2 = (2 * K, 3 * K - 1) 133 | c = random (N = N) * cross (R, R) 134 | c0 = partition (c, [cross (R, R0), cross (R, R2)], 0) 135 | c1 = partition (c, [cross (R, R0), cross (R, R2)], 1) 136 | c2 = transpose * partition (c, [cross (R0, R), cross (R2, R)], 0) 137 | c3 = transpose * partition (c, [cross (R0, R), cross (R2, R)], 1) 138 | res = numpy.zeros ((4 * N, K)) 139 | row = 0 140 | for (c, offset) in [(c0, 0), (c1, 2 * K), (c2, 0), (c3, 2 * K)]: 141 | a = numpy.zeros ((N, K)) 142 | for (i, j) in c: 143 | j -= offset 144 | if 0 <= i < N and 0 <= j < K: 145 | a[i, j] = 1 146 | else: 147 | self.fail ('connection outside mask') 148 | res[row:row + N, :] = a 149 | row += N 150 | return 2.0 * K * res # normalization 151 | 152 | def test_partitionRandomN (self): 153 | self.K = 5 154 | for _ in range(1000): 155 | res = self.partitionRandomN() 156 | self.assertEqual (res.min(), 0.) 157 | self.assertEqual (res.max(), self.K*2.) 158 | self.assertEqual (res.shape, (self.K*12, self.K)) 159 | self.assertTrue ((res.sum() <= self.K**2*12)) 160 | self.assertFalse (numpy.any((res != 0.) & (res != self.K*2.))) 161 | 162 | def intersectionRandomN (self): 163 | K = self.K 164 | N = 3 * K 165 | R = (0, N - 1) 166 | R0 = (0, K - 1) 167 | R2 = (2 * K, 3 * K - 1) 168 | c = random (N = N) * cross (R, R) 169 | c0 = cross (R, [R0, R2]) * c 170 | c1 = transpose * (cross ([R0, R2], R) * c) 171 | res = numpy.zeros ((2 * N, 2 * K)) 172 | row = 0 173 | for c in [c0, c1]: 174 | a = numpy.zeros ((N, 2 * K)) 175 | for (i, j) in c: 176 | if 0 <= i < N and 0 <= j < K: 177 | a[i, j] = 1 178 | elif 0 <= i < N and 2 * K <= j < 3 * K: 179 | a[i, j - K] = 1 180 | else: 181 | self.fail ('connection outside mask') 182 | res[row:row + N, :] = a 183 | row += N 184 | return N * res # normalization 185 | 186 | def test_intersectionRandomN (self): 187 | self.K = 5 188 | res = self.sampleN (self.intersectionRandomN, (6 * self.K, 2 * self.K), 1000) 189 | for x in res.flatten (): 190 | self.assertAlmostEqual (x, 1.0, 0, 'maybe wrong statistics %g != 1.' % x) 191 | 192 | class TestOperators (TestCSA): 193 | def test_difference (self): 194 | # Test difference 195 | self.assertEqual4x4 (full - oneToOne, 196 | [(i, j) for j in range (0,4) for i in range (0,4) if i != j], 197 | 'difference operator') 198 | 199 | 200 | def main(): 201 | suite = unittest.TestLoader().loadTestsFromTestCase(TestElementary, 202 | TestOperators) 203 | unittest.TextTestRunner(verbosity=verbosity).run(suite) 204 | 205 | 206 | if __name__ == '__main__': 207 | main() 208 | 209 | --------------------------------------------------------------------------------