mirror of
				https://github.com/pytorch/pytorch.git
				synced 2025-11-04 16:04:58 +08:00 
			
		
		
		
	Compare commits
	
		
			3935 Commits
		
	
	
		
			codex/crea
			...
			v0.2.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0b92e5c9ed | |||
| df44c571c6 | |||
| ed03f74043 | |||
| c8d8803b90 | |||
| 750245f990 | |||
| fbf573a6b8 | |||
| 9bda6dee8f | |||
| e02f7bf8a3 | |||
| 7ea48eaf7a | |||
| d278a14141 | |||
| 6b1ca4b4b6 | |||
| 65ddaf13a9 | |||
| 96156013c3 | |||
| a997cdbb25 | |||
| 8db9df94b6 | |||
| 6c9e3334b1 | |||
| b33f232678 | |||
| 058f50aa50 | |||
| 8b06efea7a | |||
| 52b7a49b37 | |||
| 47f4d549e0 | |||
| 5b6d1837c7 | |||
| 69642d4423 | |||
| 74e5328b03 | |||
| a565b77791 | |||
| daf5b20cd7 | |||
| 515efdab5d | |||
| f9f98daf11 | |||
| 2ac1003228 | |||
| 141224ad7c | |||
| ac76ab5fca | |||
| 04f31aa034 | |||
| ae59e008cd | |||
| 925208af72 | |||
| 643f8d12ff | |||
| fb8f9de498 | |||
| cb9ad7a892 | |||
| f7de7bab6e | |||
| fd97d92479 | |||
| f3aa97f169 | |||
| b0648fc3fc | |||
| be9ef9283f | |||
| 9c0d52a32f | |||
| 54545c2154 | |||
| 9ec7051442 | |||
| 2676c6357f | |||
| ef3b09fb5f | |||
| f194ac1e09 | |||
| e548580f31 | |||
| 421607a935 | |||
| 7be545292d | |||
| a0e83280ef | |||
| aa35be2032 | |||
| 626840aef3 | |||
| bcea678e7b | |||
| 1a52ca02ef | |||
| 84314859af | |||
| 9c2beb33c5 | |||
| 7deba74969 | |||
| 48bb07a4db | |||
| bb86ed7b97 | |||
| 291369ff1b | |||
| 2118400e18 | |||
| 39934da8b3 | |||
| c12b494329 | |||
| 506d52dc33 | |||
| 7687c2677a | |||
| 97d21e243b | |||
| 0bda56956e | |||
| 40af93bb57 | |||
| 9608e37969 | |||
| ec7c510557 | |||
| 8636be3880 | |||
| fb2284f3a0 | |||
| 9ec9dee27d | |||
| 7b6aab9079 | |||
| 852dd5f011 | |||
| 085abee444 | |||
| 48b85fe012 | |||
| 45ce4df74c | |||
| 5695cbf986 | |||
| 03df5debe3 | |||
| 2ebdef0154 | |||
| ceb4f84d12 | |||
| 112728cbe9 | |||
| dc17fb68e4 | |||
| 4a4d8841e6 | |||
| 3c275fe7a0 | |||
| 1978bba3e4 | |||
| 35757af6f7 | |||
| 8ab3d214d5 | |||
| ec2def803b | |||
| 71ce3448d9 | |||
| 2efac3ed83 | |||
| 66bbe5d75a | |||
| ea607afd06 | |||
| 4f035f14de | |||
| 72e9e7abf7 | |||
| eed323c344 | |||
| ea6f9a26b8 | |||
| 3719b4247a | |||
| bf1fc250d1 | |||
| 47942307b5 | |||
| 6b69723d4f | |||
| 5254846bb2 | |||
| f3f478960e | |||
| e537023147 | |||
| 09abaa2189 | |||
| 575a4a98e0 | |||
| 02e23f4f6b | |||
| 8946502348 | |||
| e708de37cc | |||
| 4af40e3471 | |||
| f417cb062b | |||
| 11f3ccf98f | |||
| 31894cafdd | |||
| 95ccbf8b0b | |||
| a5422d14c8 | |||
| 82143487b3 | |||
| bd6263c338 | |||
| f4a565ded9 | |||
| 1c6a08c1c2 | |||
| a5c2546c0f | |||
| 13e84e460b | |||
| 4d5d9de541 | |||
| 9da882e396 | |||
| 15bece50d1 | |||
| 8144f7c95d | |||
| b660303a16 | |||
| 768b7c0dee | |||
| ae3a8d5d2e | |||
| 58334a0c4b | |||
| cfcf2af95f | |||
| f3df24269d | |||
| c4120f34bf | |||
| 9755505122 | |||
| 8b42308f71 | |||
| 685ae4813e | |||
| a0fef9dd22 | |||
| 703429d49e | |||
| 567d95fa09 | |||
| 7914d67ce3 | |||
| 8451468d8b | |||
| 138b216686 | |||
| 6f6d70ffed | |||
| dc58544779 | |||
| e13704c467 | |||
| e9dd8e0e3b | |||
| a3c9054245 | |||
| c7b624651e | |||
| ba544aa0ad | |||
| 849fb1f7e3 | |||
| 16dd997239 | |||
| 1c0135b6f2 | |||
| a7d82b935f | |||
| af7aea9f17 | |||
| 366299f9f3 | |||
| 9851ef4979 | |||
| f805a8388b | |||
| 2f7b6db429 | |||
| 16203f3325 | |||
| 80d067e70f | |||
| d2874c560e | |||
| 83596bdcb1 | |||
| f3f8ce44bd | |||
| 33ac9cdc10 | |||
| 38ba935547 | |||
| 128e02d792 | |||
| 7ee7542fc8 | |||
| 52a9367fa7 | |||
| 08bb3b7cc8 | |||
| 43eaa28b9f | |||
| 7e498d2219 | |||
| d6bc2642e7 | |||
| 7d3511f5f2 | |||
| a5a8ab10b0 | |||
| 25b591eb05 | |||
| 06f94a7d59 | |||
| 027264cd64 | |||
| 7c14c377df | |||
| c674923bcc | |||
| d8fee1ebe6 | |||
| ed6f5d7038 | |||
| 9e720f1547 | |||
| ab26fa01e6 | |||
| f4ae64a6c7 | |||
| 07fcd977bb | |||
| 54cabb8bf3 | |||
| 42485d87c2 | |||
| 007d6ad816 | |||
| abd433fa07 | |||
| 6db960fbcf | |||
| 384f03f1be | |||
| c011d4f3d6 | |||
| f98c384973 | |||
| 48b797a785 | |||
| 8983bf13f4 | |||
| 20ce45b0c3 | |||
| 1e98155711 | |||
| 1c14178c65 | |||
| 37183e91de | |||
| 14337693d0 | |||
| 58e4caf80f | |||
| b900a49308 | |||
| c888857461 | |||
| 7053b84c0e | |||
| 8304dc4d68 | |||
| c48d50a2e2 | |||
| 41abcd4b41 | |||
| 703ccbb8cb | |||
| 27da4eafc2 | |||
| 459cb697b5 | |||
| ce96b84ccb | |||
| feddb03d58 | |||
| fe3802d724 | |||
| b8d0c7fc0d | |||
| ea563c1df1 | |||
| 841173c530 | |||
| f4c502e8a8 | |||
| 593c5e12e1 | |||
| dc2ed7fd33 | |||
| 81fd2bf2d0 | |||
| 8915e2710c | |||
| ebd5c085dc | |||
| a9759ef401 | |||
| f899eafe85 | |||
| 169ca67a4e | |||
| 41c8fee3e7 | |||
| bb891758bf | |||
| 7c10f1b932 | |||
| a20729244b | |||
| a74fb22b9a | |||
| 0d91048639 | |||
| 10e23943b3 | |||
| be18499e85 | |||
| 1037f30e41 | |||
| 78ecc2d3b1 | |||
| f483679425 | |||
| dfd5d8d0fe | |||
| 158c7e86dd | |||
| 73128f7b08 | |||
| f536c662bf | |||
| 2ecb18881c | |||
| 9d8cff9bc1 | |||
| ab3d85c410 | |||
| e58e27cf16 | |||
| 3314d51dcc | |||
| 1ef1dd9cad | |||
| 98206c326e | |||
| 9d0c674cb7 | |||
| bff762c3ff | |||
| 10a8ccf27f | |||
| 0a9e8a23ef | |||
| 8b003565ec | |||
| 53ac2d46c6 | |||
| 318ea29a86 | |||
| ab3a9e177e | |||
| 46a868dab7 | |||
| 581921f696 | |||
| 0025e1c776 | |||
| 9cba97a833 | |||
| c6d7e1e6bf | |||
| 49f679d0e9 | |||
| f0788afb0c | |||
| a4dc7dcd04 | |||
| 5dd05ed8ee | |||
| 0a34f05d5b | |||
| 4fda678a85 | |||
| ebdec9a837 | |||
| c3c7845572 | |||
| 90d0762d14 | |||
| 73fead9f8f | |||
| 3748b6d3eb | |||
| b3589b04fd | |||
| 5964394a4c | |||
| 1aaa24d99b | |||
| 295ed7e264 | |||
| ab7d4e2bce | |||
| ae65236490 | |||
| c2069a15e0 | |||
| 56df97ce93 | |||
| 89c682dfb9 | |||
| ae839f4b2e | |||
| 05c2bafc9d | |||
| 0dbf871d9e | |||
| f425c5216b | |||
| 635bb5ec9d | |||
| a7f6b0ab4f | |||
| e5bac2dd2d | |||
| ec8da55a7d | |||
| b4414c0dc3 | |||
| 39edc378fb | |||
| f6578c1b24 | |||
| daa84e7663 | |||
| 1aa145dbac | |||
| d4b8834131 | |||
| 699d1ec7fb | |||
| 05062a1439 | |||
| e187ba7a9f | |||
| 35ed224d04 | |||
| 72b292d45c | |||
| 5b4cd9bb49 | |||
| c691fc6dc7 | |||
| 42cf68b402 | |||
| 8a65ef1098 | |||
| 406040f6a9 | |||
| e26139b7f7 | |||
| 457587088a | |||
| da0fad8a7a | |||
| 2c038f2074 | |||
| b3e500c522 | |||
| b3f6ff1b3d | |||
| 6df23b418d | |||
| e5b5154768 | |||
| bfaddc0a19 | |||
| 4d5075add2 | |||
| 0a95613cef | |||
| 8a4eb50ed1 | |||
| b5e1df046e | |||
| 08648061f7 | |||
| 4c35c630ec | |||
| 3744efeaf8 | |||
| bc032be13e | |||
| f814a892cf | |||
| d592e188f7 | |||
| ae61f3ff42 | |||
| 1f391a42f7 | |||
| b933423495 | |||
| ee1b7b50b3 | |||
| 7cdd018db4 | |||
| 7806a09f03 | |||
| 7523c49f03 | |||
| 32e666551a | |||
| ab0c321f80 | |||
| 9db14936eb | |||
| e5857c5f1c | |||
| 7da77c4255 | |||
| 656cb1c31a | |||
| 4ab4938cf0 | |||
| 1324c4b081 | |||
| bb3779efe8 | |||
| 7c24a3d5cf | |||
| 194bc404b5 | |||
| a9ea975977 | |||
| b1a84e3c70 | |||
| 8a24f2b4d8 | |||
| 66d93b60b3 | |||
| 2af6ba3b2a | |||
| b59b44fac7 | |||
| a10a1c92b1 | |||
| bb6908e163 | |||
| c555cd8253 | |||
| 5e078bb7cc | |||
| ee10e7457f | |||
| 7cd6cc17af | |||
| 8bfef60b07 | |||
| a45ad7cfba | |||
| 93e05eb458 | |||
| 32fd4a3d60 | |||
| f09027bc29 | |||
| 9a196829e2 | |||
| 43dec0a210 | |||
| 064ef8b81b | |||
| 662faf7c41 | |||
| 104234a6a8 | |||
| c16a268f47 | |||
| cb4eaa9c5d | |||
| fb32164a72 | |||
| b5854a11c4 | |||
| ddbd4ef4ac | |||
| eccc759c36 | |||
| fecd05ba2f | |||
| a7d1cd75ec | |||
| 497db732fc | |||
| 81e14ad2de | |||
| 93a7c9de29 | |||
| 96febbb762 | |||
| 62cfc94f44 | |||
| 3f6cda8696 | |||
| a836f8f56f | |||
| 278cbbae49 | |||
| 68cbb857f2 | |||
| a1c557bc45 | |||
| 4c5b7d41ba | |||
| 13e7648fd1 | |||
| 1572173ca7 | |||
| e16ceef76a | |||
| b79ff11aca | |||
| 50c0912a75 | |||
| c3ad55f746 | |||
| 4b93f32234 | |||
| 03f41c8120 | |||
| e0b70d0f64 | |||
| 0b2b7d0594 | |||
| 6d97ac0c0f | |||
| a405efa756 | |||
| 67968cb60b | |||
| a6c5e3f2e2 | |||
| 6ee6b4980b | |||
| ceb13c8cc3 | |||
| 82ef292f00 | |||
| 76ee014d10 | |||
| f619ac6ac9 | |||
| 32e6372538 | |||
| 172a356668 | |||
| 329a2f7d27 | |||
| 69e38ee821 | |||
| 38e6b9c7e7 | |||
| 7775e9e777 | |||
| 293262b8f1 | |||
| e66e01a2a0 | |||
| 0a93903e8e | |||
| bcac55dd2f | |||
| 6cdcd9c603 | |||
| 075030d974 | |||
| 23dec70614 | |||
| fc0ab229ad | |||
| ce3bc5a4a5 | |||
| 3dbece7eb5 | |||
| bd94718c87 | |||
| 2f8d21a7f2 | |||
| 4f4fc9091a | |||
| 7ee095cf7f | |||
| 462ab8a644 | |||
| dd5c7c473f | |||
| 6dca309017 | |||
| f945fbc3dd | |||
| db70d4d223 | |||
| 7714b5a088 | |||
| 860f51e67f | |||
| 2c04ce63a5 | |||
| 83bfa5e1ab | |||
| 618f20fb38 | |||
| 9711223c12 | |||
| 7d0f1c51bb | |||
| 7560474fbb | |||
| e69fe5bdb0 | |||
| f3ae90e329 | |||
| bfdd1f2199 | |||
| b164efb8b0 | |||
| 94c7260087 | |||
| aac459431b | |||
| a04d1af0a4 | |||
| a54a7c1312 | |||
| 9ba799c26b | |||
| 5cfb1329b5 | |||
| af2dd0d3e9 | |||
| 79a343bbd4 | |||
| 88e4bec8fa | |||
| faa7c2cc2c | |||
| 3cecdf84f1 | |||
| 49586d9556 | |||
| 8d33603901 | |||
| a64560c22e | |||
| 97f50edf46 | |||
| 86a96cd759 | |||
| f61ec2495e | |||
| d605afe8b5 | |||
| 909f31764f | |||
| ea5819045e | |||
| 9c53c6dcb9 | |||
| 9d916e561c | |||
| 4e356528b4 | |||
| 9fd354e643 | |||
| c8e9bc493b | |||
| 6de5ce6bac | |||
| 38b9598685 | |||
| 244af06adc | |||
| 3ada9da808 | |||
| 5a63a6d47f | |||
| 38a48729f0 | |||
| deb0aef30c | |||
| 3977ee3520 | |||
| c0e7bda3f1 | |||
| df412051fd | |||
| 7bee03fe1e | |||
| 865beada0e | |||
| 6a46863c83 | |||
| d763db59a9 | |||
| 5d6e593c67 | |||
| bac408b693 | |||
| 2f967a204c | |||
| 1a6995b28c | |||
| 122dd9e8ec | |||
| 7c024e93c6 | |||
| b4698d6d1d | |||
| d9d50f80c7 | |||
| 714351ff39 | |||
| 6f51b4ce2d | |||
| 12813b88f6 | |||
| 23ab9d481a | |||
| 8db8716c7c | |||
| b37f18be53 | |||
| 5a0d5ec058 | |||
| 095ddc7d08 | |||
| 86a065e45b | |||
| 59d438de2e | |||
| 6626881e7a | |||
| 49ec984c40 | |||
| afaad94fed | |||
| 4f602a52b5 | |||
| 3abc8be42c | |||
| f4ce99fd87 | |||
| d5a0f97ea7 | |||
| e8ec4110f6 | |||
| ffd808768e | |||
| 5b81746767 | |||
| d49b73bbe6 | |||
| 7040b82ede | |||
| 723819014e | |||
| 1ef4cc1591 | |||
| deec86cc05 | |||
| 7da46097fe | |||
| 21d9b0c9dd | |||
| 69287250d1 | |||
| 74a23c5aba | |||
| 177785eecf | |||
| ad9604f45a | |||
| 65b23f146e | |||
| c54e532954 | |||
| ec120fac0c | |||
| e06523482a | |||
| d6fb92fec9 | |||
| 5e1a714386 | |||
| be65f46c76 | |||
| 3556d1b8a3 | |||
| 5af46cb352 | |||
| a36f95fe26 | |||
| cd35091d9b | |||
| 3c586d196a | |||
| 8e2f347951 | |||
| d279c6e099 | |||
| 014372e707 | |||
| 92fde6cf06 | |||
| b44ea57ba8 | |||
| e96f854ce2 | |||
| edf2969bd8 | |||
| e653fe2857 | |||
| 70c33777a6 | |||
| 471dfe9791 | |||
| 85d838a028 | |||
| 6a40acb4f0 | |||
| 9087624634 | |||
| e772a440cb | |||
| efd8b54be2 | |||
| 54c3441e9c | |||
| 7d1b042cb2 | |||
| e45c1046fe | |||
| a563ce1105 | |||
| 92d52bf395 | |||
| 0463ddf16b | |||
| 9060e6be7f | |||
| f0b8c4821b | |||
| 0f79bf1a69 | |||
| 503002eda7 | |||
| cf55e1e48a | |||
| 8d35d4215b | |||
| 9356640453 | |||
| ae6b8d0112 | |||
| ec2f6a81fd | |||
| 1f9a365fdc | |||
| d38a87217f | |||
| baa4ba973b | |||
| a24db91a38 | |||
| e3d5826b92 | |||
| ba690d5607 | |||
| 5f1a16a018 | |||
| dcf07a2d7f | |||
| fab5bef9f6 | |||
| 21a5c8ea5e | |||
| 5300aafc1f | |||
| a9bd1de9e9 | |||
| e57eef4bcb | |||
| d81da41650 | |||
| 62835fc3f5 | |||
| da7957c660 | |||
| 2a49353d5e | |||
| b05c23de44 | |||
| 019e967113 | |||
| b9ab26765e | |||
| da45b4c6b3 | |||
| edd41d8d80 | |||
| 352f8b2fa6 | |||
| ced01f6c91 | |||
| d351239c10 | |||
| 1b1579c89d | |||
| df7c47142d | |||
| b96f76e470 | |||
| 7e62971c86 | |||
| a7d987544d | |||
| 71ccedbc6c | |||
| c3cda260b6 | |||
| 22949350b6 | |||
| 3f7b48ccda | |||
| d7db75c10f | |||
| e50d599240 | |||
| c6a6391c38 | |||
| d50ad408fa | |||
| 73ccdb3920 | |||
| b6c75c43c8 | |||
| a53cde09b5 | |||
| 98afdcf409 | |||
| ef32e96447 | |||
| b032b88f34 | |||
| a76098ac15 | |||
| 2ce5875a4d | |||
| 511cb20e7d | |||
| e3305eb9dc | |||
| e9bf702c5e | |||
| 9a2d11dd36 | |||
| 3716286e6b | |||
| c357ebd590 | |||
| 85a95d8a23 | |||
| 6422ea3d9f | |||
| ddf6328990 | |||
| 174c3cc399 | |||
| 24aecaa2c8 | |||
| 4853cc0194 | |||
| ac1c674723 | |||
| eba3dc8561 | |||
| ee9d4d58e2 | |||
| b7c4900d19 | |||
| e22f9036de | |||
| c01ff1f3dc | |||
| eeb8e5c31b | |||
| c6c9e61169 | |||
| 34804e9600 | |||
| c41555fb0a | |||
| 96cc1e1ac7 | |||
| cfdd49f76a | |||
| 447d9287bf | |||
| 832eaf900b | |||
| e685277299 | |||
| 8ea7c87c29 | |||
| 09c0d9c51c | |||
| 240384605c | |||
| 9f9a3d596f | |||
| a8c26c1040 | |||
| 6cdfe0d7b9 | |||
| 1b66b50064 | |||
| cf42c1a044 | |||
| f717f29d7e | |||
| 181d2f41bd | |||
| 2059ece284 | |||
| b3e100b40e | |||
| ec2de16776 | |||
| ea05d6aec3 | |||
| 5a93d6b903 | |||
| 75e0df271a | |||
| 565bf7116b | |||
| f1c57ace1b | |||
| 460b8715a8 | |||
| 6da111c53d | |||
| 568c5c91ee | |||
| 00843c57c9 | |||
| 501467db17 | |||
| d51cd61e2e | |||
| 447fe953e5 | |||
| 7b5af7d1b7 | |||
| afc26ac675 | |||
| 6f791e74f1 | |||
| 3106423713 | |||
| 4eb448a051 | |||
| 065c59860a | |||
| 45f665d05c | |||
| 64faf120ac | |||
| 0b74f0d796 | |||
| 8074180081 | |||
| 5ce4a4adbf | |||
| 3e9caed731 | |||
| 7b578dd68e | |||
| 3f1f3f9734 | |||
| bd705d38ce | |||
| 630af4d7d8 | |||
| 0409b42a02 | |||
| c39d48ea7d | |||
| 3abe5c80d2 | |||
| 05bc877a05 | |||
| 7ea9d9af4e | |||
| 6a7c56499c | |||
| 46ee1e4687 | |||
| e63b49d9ab | |||
| 036c3f93af | |||
| 4f261f5730 | |||
| 98581b9f7e | |||
| 9a497f824b | |||
| 1e63a04a18 | |||
| e54112758c | |||
| e1d257bc6d | |||
| 3d38e4f126 | |||
| fa93653d09 | |||
| ff047fdeef | |||
| 2486a6bbd0 | |||
| 640846b864 | |||
| ba56de1150 | |||
| 6e3e453ad2 | |||
| f5d919a685 | |||
| 02e4ca9cab | |||
| 70a774898e | |||
| 49befe3fcd | |||
| 7eac2073b8 | |||
| 45524ec33c | |||
| f072c74dfd | |||
| 2acfb2376a | |||
| 0c5598c668 | |||
| feaee29bfe | |||
| 7f6cd7c7ea | |||
| 625850c2c2 | |||
| 9b3447761a | |||
| ed679fc43c | |||
| e6c9509a41 | |||
| c57f0530e7 | |||
| 8021bb938c | |||
| 1f4317be3f | |||
| cba46a4869 | |||
| b391f53681 | |||
| 85732b52ec | |||
| 156fe28666 | |||
| 2f4bf4ab39 | |||
| 1f3ff5ced2 | |||
| b8b7f879c2 | |||
| 7b10b16496 | |||
| da86633c7c | |||
| c573d53939 | |||
| cb79c24d0b | |||
| caa1cdf0ce | |||
| 368ecb47f9 | |||
| 6107d15d14 | |||
| ba885a1a51 | |||
| ce1a0eb6c9 | |||
| 7afd78d77f | |||
| 6b84dc26f0 | |||
| 0f458ee3c4 | |||
| 8aa011f52a | |||
| 2a610c9d13 | |||
| 0ba20435ce | |||
| 6fc9130052 | |||
| 28f4f6db2c | |||
| 9b2de027be | |||
| 78abf0134d | |||
| 9db7787316 | |||
| efa913b1c2 | |||
| d1a4467682 | |||
| 507ddc4cde | |||
| aba05ce9db | |||
| be843eb26b | |||
| 5bb13485b8 | |||
| a86adf43a1 | |||
| 1c304a9ef6 | |||
| feef54ec34 | |||
| 5026209d0c | |||
| e7220380bc | |||
| 9fa0e403d6 | |||
| 35cf380ed1 | |||
| 3a7e068439 | |||
| 862105ec8b | |||
| d5e821044a | |||
| bfc8a3ebba | |||
| 6fab62173e | |||
| c4742fd128 | |||
| e124790cb2 | |||
| 171638a451 | |||
| d95f711501 | |||
| b9e00dfbb8 | |||
| f6a00fac13 | |||
| be5191a00b | |||
| c9d8e0a43a | |||
| ae2b2cbbec | |||
| f4cf1d6d18 | |||
| c34cff7035 | |||
| 194d7408bb | |||
| 0d538246fb | |||
| 7c3cb24485 | |||
| af790f86f3 | |||
| 5f308b50fb | |||
| 98dbdc464b | |||
| e70164316c | |||
| 33b3968660 | |||
| 91a118c116 | |||
| 0764589ed1 | |||
| 27671c800d | |||
| d0504aa41d | |||
| 008a8c9720 | |||
| 105df5844d | |||
| 50bf7d5cbc | |||
| 066fbcd014 | |||
| ecf29f10ad | |||
| 22bbd7ac33 | |||
| 2075abbe30 | |||
| e694db0eeb | |||
| c5ae79fe4e | |||
| 4ad2e155bc | |||
| 6d693fe413 | |||
| 23b556ef77 | |||
| e3f41a4962 | |||
| 90e9f8a476 | |||
| 5f15a9e0cb | |||
| ff0ff33a11 | |||
| eb2c6ea874 | |||
| e64b2e1cd7 | |||
| 7d40140bfb | |||
| e50c7daaf9 | |||
| 600f366a13 | |||
| 4e18d89791 | |||
| de9845588d | |||
| c061ed5bda | |||
| e9d648c5e7 | |||
| 80c0a8776b | |||
| 4ec0435b39 | |||
| f8be3a20d3 | |||
| 7b21b0b6d7 | |||
| 0910e0ac90 | |||
| 93094294ba | |||
| 743e4894d2 | |||
| f273377d19 | |||
| 836332e0a1 | |||
| f1591fade5 | |||
| 2e7635b929 | |||
| e9953c4595 | |||
| 72e8190994 | |||
| e1278d4ee2 | |||
| 66bd200de0 | |||
| 574cfe3cf3 | |||
| 699755e04f | |||
| fb07914c0c | |||
| aa2ee86375 | |||
| ecd51f8510 | |||
| 5aa1f769d3 | |||
| eecc807a75 | |||
| 5386012164 | |||
| 4bf813e068 | |||
| 3b4bc721ef | |||
| dca208b525 | |||
| 181cb15c72 | |||
| 7df8fbb64f | |||
| 5c7453447f | |||
| 87164f554d | |||
| 267e7c0431 | |||
| e5db8f98be | |||
| 20aa5b066f | |||
| de9998e198 | |||
| 702a2e3bc5 | |||
| 2ca787fcf4 | |||
| 2ec629bef9 | |||
| 2197e4c766 | |||
| 2a28283680 | |||
| 4624278b1d | |||
| 79d4ac670c | |||
| 4ebf3ff46d | |||
| ac3ba9a2ad | |||
| 14e1bfddbc | |||
| c19fbd3364 | |||
| a17d96d571 | |||
| b7dcc29430 | |||
| 18b4dcd28b | |||
| be81304d27 | |||
| f07f13c6e9 | |||
| 310d08c37b | |||
| 234df2138a | |||
| 2b340e7d50 | |||
| 6888c61fa8 | |||
| ba3328b365 | |||
| 3b4fe5dfc4 | |||
| ce42761628 | |||
| df4791d6c0 | |||
| 7e8830c3d5 | |||
| b91cec7f66 | |||
| 765aeb1a08 | |||
| 280e2a94e5 | |||
| e7f453b5de | |||
| 8030aa0f1b | |||
| 40ad2cde62 | |||
| af4a978c44 | |||
| fe5fc6723f | |||
| 6e6179633b | |||
| c97e60c45d | |||
| 2cdb368f97 | |||
| a5b2f3461a | |||
| d3e60599d2 | |||
| 98d8e0b040 | |||
| fe2c360eda | |||
| 59ae109bbb | |||
| 8623076654 | |||
| a362b4f367 | |||
| ef724e355c | |||
| e863d27393 | |||
| 4c388f9398 | |||
| 6740d1d904 | |||
| f891d9b1bf | |||
| a81f330854 | |||
| c02241edbd | |||
| f30a92fa17 | |||
| 1391ff99f4 | |||
| 43019bd88a | |||
| d6380910f5 | |||
| 04491e84e4 | |||
| e247249a5f | |||
| 0160438eb9 | |||
| 7dd8571bc6 | |||
| 48a7869b23 | |||
| 582fd3db7d | |||
| 9169f60a84 | |||
| 457d78a7d9 | |||
| a071ccbea6 | |||
| db1eb66456 | |||
| 45020a74cd | |||
| 9c01f5d6b2 | |||
| cbb9f08b71 | |||
| f75ab857b8 | |||
| f2903332c7 | |||
| 9643be76f9 | |||
| 4f09461d24 | |||
| bafb2e5cc2 | |||
| 28a7fbbdf5 | |||
| 4c1cdb6148 | |||
| 775481ed56 | |||
| 5b2aac7c73 | |||
| 224f5eabf5 | |||
| fd490c6490 | |||
| d6a31c68a0 | |||
| 96a281dfab | |||
| 94b147fd41 | |||
| c26f6877a0 | |||
| 8908000262 | |||
| 8b1d5727d8 | |||
| 75f1989bec | |||
| e221536ad8 | |||
| a44317fea8 | |||
| 24e5a9057e | |||
| 060048bcd8 | |||
| 77035d151e | |||
| 50c9c23525 | |||
| 3f81803b09 | |||
| d421c473a9 | |||
| 48f9e526ea | |||
| 69574a6dc4 | |||
| 928f6516c1 | |||
| b93b525a1c | |||
| 8db2cf6182 | |||
| 7e8ef0e22a | |||
| 27990fee54 | |||
| 2ef7331007 | |||
| c2cfa4cf5b | |||
| c915f8ddbf | |||
| b39a2f2cbb | |||
| d9f01397b3 | |||
| 8ca7bf2ab3 | |||
| 8950f41da3 | |||
| afd01164f8 | |||
| a123247240 | |||
| 41705ce7d5 | |||
| 88fc1d39ff | |||
| 9899512401 | |||
| d95feb3feb | |||
| 3ab074b3c5 | |||
| 6a69f7007b | |||
| 71b9dea6ec | |||
| fa4f363b93 | |||
| aab30d4ea2 | |||
| 2b56711c24 | |||
| 2fa3365f94 | |||
| 5224fc56b0 | |||
| 4373580e6b | |||
| d9406a8a1a | |||
| e80a3a7f7b | |||
| 5b83fe6781 | |||
| 24d92b5d9f | |||
| 1375694853 | |||
| be5e399d46 | |||
| a782a6231f | |||
| e788ea40de | |||
| 6089900011 | |||
| 81345306c8 | |||
| f0a19e2617 | |||
| 8236d38e81 | |||
| 8adf8fe2ed | |||
| d2472d1ab5 | |||
| 331219c550 | |||
| 7805ac9098 | |||
| f9149b1f2e | |||
| a8e6610e3d | |||
| 56cc1e219b | |||
| 1607042bf4 | |||
| 7d023cda6c | |||
| 9e8b4ef075 | |||
| a35f507532 | |||
| 6aa22beb86 | |||
| 71bf8fb55b | |||
| c7d83a16f6 | |||
| 934816c01c | |||
| 5a0510934f | |||
| fc19473501 | |||
| 34546f022a | |||
| ab77742f6e | |||
| 701e63107f | |||
| 655c22569e | |||
| cd3bbc9dfd | |||
| 1018b238ac | |||
| e27bd4ce7a | |||
| b2acc33c73 | |||
| 40804830b8 | |||
| 01d84c5f9d | |||
| 88b42324e7 | |||
| ec260fe8e9 | |||
| 328b416068 | |||
| 4bde9efbd7 | |||
| ff781ed059 | |||
| 8f9a1af253 | |||
| 31900b6bae | |||
| 46cf6ff5fb | |||
| fcf4deac7d | |||
| 2ca071d730 | |||
| 8a901c510d | |||
| ed60fe0ed6 | |||
| f67ab32d34 | |||
| 9150e33765 | |||
| e4478804ce | |||
| a220f2c3aa | |||
| 15267ac009 | |||
| 0cb60e7d5a | |||
| b61174047f | |||
| 8d93fcf13f | |||
| a559893c9f | |||
| 50c2759afe | |||
| cb66e9cf78 | |||
| 735f5af87e | |||
| c852883086 | |||
| ab77e4c3d7 | |||
| 2444278b8b | |||
| 62c584ba79 | |||
| fbd53d87bf | |||
| 71303b8af4 | |||
| 4336e9ea66 | |||
| d48afd41f9 | |||
| e21e4bf3e8 | |||
| 8e36339911 | |||
| 5391fe8953 | |||
| 0925c91e80 | |||
| 253c854da5 | |||
| 7c59754d24 | |||
| 2bf7dc643f | |||
| ce30c76823 | |||
| a8d60ad3ac | |||
| aec658f870 | |||
| 01a35dcace | |||
| afeeb81e79 | |||
| 6002f94232 | |||
| a5c7d98611 | |||
| 605b3c86ce | |||
| 2087b1157a | |||
| 81e972031d | |||
| e9ff57176b | |||
| a739960515 | |||
| f43320dbf2 | |||
| cfa504691c | |||
| 0b50f794e9 | |||
| 2abbb5133c | |||
| fcf8387779 | |||
| ade105fb7c | |||
| 4e693d12ab | |||
| 79c4cb96b1 | |||
| 97bd6aae37 | |||
| f618ea9f31 | |||
| f6fef3718e | |||
| 3fcdd6a42b | |||
| 707c1ca4cc | |||
| bc0ed9298d | |||
| 040cf42643 | |||
| 64ee4056d7 | |||
| 55d69b5ade | |||
| 0d7d6e1f0d | |||
| b16a352a3b | |||
| 88bcfc1531 | |||
| 662163bef6 | |||
| 4026593240 | |||
| a931064a52 | |||
| 441d75ce56 | |||
| 3de56785fa | |||
| 5ee8536a02 | |||
| f00a5d2f54 | |||
| e48db02e10 | |||
| 7f2553bc6f | |||
| 66a20e5c32 | |||
| 37d95687c4 | |||
| f0c7124420 | |||
| ae1c365dbd | |||
| 6fd9b53d93 | |||
| e692c38fcf | |||
| 5dfa73702f | |||
| 95140094cb | |||
| ef95926103 | |||
| e7f5220dfa | |||
| a7ae04a657 | |||
| 7f03182bfa | |||
| 9f2a5d804d | |||
| aa506fa4d7 | |||
| 955869a09a | |||
| d82cad3019 | |||
| 9504246c32 | |||
| 81cf3dbf79 | |||
| 12f1b4f76c | |||
| 84bdbe5ab4 | |||
| 85954032d9 | |||
| 1a04b92226 | |||
| 8a822d48f5 | |||
| 5511ad258b | |||
| 75a635630d | |||
| 8e6524938b | |||
| 4e4cfd8b2b | |||
| 6bd4ecd153 | |||
| 5c802c5ba9 | |||
| 04f5b5ea83 | |||
| 5b40e4245d | |||
| ae5865082c | |||
| f86beccc5b | |||
| d122b4e4ec | |||
| ccfc4567dc | |||
| 81008aa111 | |||
| 0cdf10478d | |||
| 4de82cfa0f | |||
| 1ac8251373 | |||
| 511ca3ea1b | |||
| 8ce1382e99 | |||
| 22cdef3ddc | |||
| 148b11847b | |||
| b3a2f30715 | |||
| 91c4ba7980 | |||
| 03f1cab801 | |||
| fa2c566353 | |||
| 2d1122739c | |||
| 7861f585fe | |||
| 3abf2ef225 | |||
| 70c4b82eba | |||
| 274b5c9003 | |||
| dfa2d26830 | |||
| 559ae078b8 | |||
| 030ff4928a | |||
| 0829bffdec | |||
| ffc7911bec | |||
| ff1fde6151 | |||
| a216e377b3 | |||
| b13b7010b9 | |||
| a3bfb9f376 | |||
| 5c79046d39 | |||
| 30fd222b80 | |||
| 3b7b23df66 | |||
| d933287114 | |||
| 761eef1f19 | |||
| d8ae7893e0 | |||
| 90b872c670 | |||
| a95ce9e98f | |||
| b8ccf42c74 | |||
| 8aa1cefed8 | |||
| 4b147e2079 | |||
| 0d908d813b | |||
| 1c391f6f93 | |||
| be146fd721 | |||
| 2979f4b989 | |||
| 22b3600f19 | |||
| 215813d7ac | |||
| 80e88a88ed | |||
| dc7695a47a | |||
| 032a65edff | |||
| 55546359b6 | |||
| fe3d5a63f2 | |||
| e4b4e515cd | |||
| 4b1f5f4bd6 | |||
| 37718e207d | |||
| afd576ec0e | |||
| 95aa2af377 | |||
| 6774d39c96 | |||
| 567faedc59 | |||
| 7c2c7e8e31 | |||
| 3eab8a71e2 | |||
| 2fd4d088ff | |||
| 5d274cd499 | |||
| 8051dec608 | |||
| f2c1071c33 | |||
| bb71117ecc | |||
| d25433a099 | |||
| 7dd45490f8 | |||
| bf632544e6 | |||
| 282402d4f3 | |||
| 1461709ea0 | |||
| cce03074f5 | |||
| f2f63773d8 | |||
| 84aa41824c | |||
| 25c8a117af | |||
| ae122707b5 | |||
| b4fe5ad641 | |||
| 5a761dbe65 | |||
| dd893391d5 | |||
| 649f04d077 | |||
| f45ef5fdb8 | |||
| e8196f990d | |||
| 269b77a1b2 | |||
| 476d85dd3f | |||
| 63f6c0d692 | |||
| b546fa3fcd | |||
| 1d656b6769 | |||
| 3acbbb30f2 | |||
| a65e0f488c | |||
| 8dc5d2a22e | |||
| ed97f3f854 | |||
| a231fe8fc5 | |||
| bb353ccc17 | |||
| ced0054a9e | |||
| 68ee5ede29 | |||
| 2966e3295d | |||
| 4df98e2927 | |||
| 6ccac5ce28 | |||
| 3865606299 | |||
| d3334db627 | |||
| 50f5a4dd18 | |||
| b60936b9ae | |||
| 2d750b9da5 | |||
| ca376d4584 | |||
| ef183a1d23 | |||
| f4d8944973 | |||
| 6b7aef63ac | |||
| b3ab4b1094 | |||
| 1e8cb82a2d | |||
| dd399a8d68 | |||
| faac0f5c25 | |||
| c36f47bd1e | |||
| 3d1888cd95 | |||
| 97a82a3018 | |||
| 5cd313ed23 | |||
| b414494035 | |||
| c10efc646e | |||
| 348531ad8d | |||
| 9d83121ef5 | |||
| 6d7cb31e53 | |||
| 30a9cf7a46 | |||
| 714b2b8bf6 | |||
| fe4bd5066b | |||
| b9aef6bc03 | |||
| 0056b08834 | |||
| bd0df61bb5 | |||
| d9678c2e34 | |||
| b3c0aa3b7d | |||
| 8fc9c79287 | |||
| 4fce1a389f | |||
| 8ce56c30d4 | |||
| 4667f936e3 | |||
| 4eaa30b634 | |||
| 77fbc12f23 | |||
| 7e46eb1613 | |||
| 821656d2d8 | |||
| 86e40ed875 | |||
| 1d0699e147 | |||
| b9379cfab7 | |||
| f0b75c4aa4 | |||
| 7654b3f49e | |||
| 37ebbc2809 | |||
| 8241cd7b6e | |||
| a7781fdebc | |||
| 29ddbc3e37 | |||
| 16a133ed9a | |||
| 1aa665f6a8 | |||
| c4d1318662 | |||
| 379ae6d865 | |||
| 24376ff9d3 | |||
| 6ac793dcbe | |||
| e00d9c1fd8 | |||
| be6322e4b5 | |||
| 62063b2f62 | |||
| 13b1580613 | |||
| fe788f5003 | |||
| e50a1f19b3 | |||
| e86db387ba | |||
| 1bf61b8adc | |||
| 704ee3ca68 | |||
| 9004652c7b | |||
| aca6ce984c | |||
| ed8773f7bd | |||
| 0f7b7b27b1 | |||
| 48f48b6ff2 | |||
| 615b27eadf | |||
| 86ede33035 | |||
| bd09055207 | |||
| 4bd220d91a | |||
| 170d790b66 | |||
| e216f557fd | |||
| 997312c233 | |||
| d602b3a834 | |||
| f531d98341 | |||
| 6bdd5ecaf5 | |||
| bfbde9d6eb | |||
| b9c816a796 | |||
| 2f5c215d34 | |||
| 01650ac9de | |||
| ce536aa355 | |||
| fc0af33a18 | |||
| c7c4778af6 | |||
| d873077349 | |||
| 0c38827318 | |||
| fb766c00b3 | |||
| e600c9830a | |||
| 73a65cd29f | |||
| b785ed0ac0 | |||
| b2d077d81d | |||
| 4814b0bc09 | |||
| b1c2714ad5 | |||
| a462edd0f6 | |||
| c2425fc9a1 | |||
| fbcedf2da2 | |||
| 3d95e13b33 | |||
| 228e1a8696 | |||
| be0e8c0009 | |||
| 3fa8a3ff46 | |||
| 4647f753bc | |||
| 7ba5e7cea1 | |||
| 9b626a8047 | |||
| bd0e9a73c7 | |||
| 7bddd586f7 | |||
| da10450535 | |||
| 2b1cd919ce | |||
| 8e46a15605 | |||
| 15a9fbdedb | |||
| 6336300880 | |||
| 5073132837 | |||
| 65b66264d4 | |||
| 0f872ed02f | |||
| 761d6799be | |||
| 0d179aa8db | |||
| 5b171ad7c2 | |||
| ac9245aeb3 | |||
| 60736bdf99 | |||
| 7d58765cee | |||
| 76f7d749e4 | |||
| 0b7374eb44 | |||
| 6fff764155 | |||
| 8ced72ccb8 | |||
| b1ae7f90d5 | |||
| 8b61ee522e | |||
| 76ca3eb191 | |||
| fea50a51ee | |||
| 51e589ed73 | |||
| 2e87643761 | |||
| ba9a85f271 | |||
| a22fd7194e | |||
| 0714d7a3ca | |||
| fb7bafdd0f | |||
| 34ce58c909 | |||
| c238ee3681 | |||
| e1d7eaf7d8 | |||
| f5338a1fb8 | |||
| d96ad41191 | |||
| f17cfe4293 | |||
| aec182ae72 | |||
| c93c884ee2 | |||
| c42a2d4d24 | |||
| f89252c336 | |||
| 490c15fae9 | |||
| 7e3b572ca7 | |||
| 5fbcd88102 | |||
| f2d72ba10f | |||
| 2108b42b92 | |||
| bae8df62d3 | |||
| a2b2880cc2 | |||
| 70fc15c05c | |||
| 98775b6bb4 | |||
| b7cc2a501f | |||
| 0720ba53b3 | |||
| ff5fa11129 | |||
| 837023bb4f | |||
| e88d241757 | |||
| ecb37e4439 | |||
| 0c88194807 | |||
| 50e73a8313 | |||
| fc7f026980 | |||
| 9f18f83375 | |||
| 9c114e6f1c | |||
| 0e78a59610 | |||
| 5e7f5db332 | |||
| b5f7592140 | |||
| f366e5fc81 | |||
| 48f087f6ce | |||
| 7fef264bfa | |||
| 8996811936 | |||
| c219a183d0 | |||
| 8e1d6f9b60 | |||
| 7ad948ffa9 | |||
| 3277d83648 | |||
| 1487278fdf | |||
| 977630bc15 | |||
| 12efd53dba | |||
| 37e05485d9 | |||
| c76770f40e | |||
| da725830c2 | |||
| fc6fcf23f7 | |||
| b190f1b5bc | |||
| dfca8dfdc5 | |||
| b46d5e0b04 | |||
| f19a11a306 | |||
| cfcf69703f | |||
| e22b8e0d17 | |||
| fbfba6bdca | |||
| 3cc89afde6 | |||
| 1e4aee057c | |||
| 8dfcf7e35a | |||
| 76de151ddd | |||
| 2676cc46c2 | |||
| 1bf7bc9768 | |||
| 3c41c9fe46 | |||
| 6ff7750364 | |||
| 4d25c3d048 | |||
| 267b7ade50 | |||
| 5ca6516ecb | |||
| 67f94557ff | |||
| 61bd5a0643 | |||
| 748d011c8b | |||
| 5d5cfe2e57 | |||
| 7cbe255296 | |||
| 4ef303698c | |||
| 83e8b3f6c3 | |||
| 502ebed796 | |||
| 68ff58d771 | |||
| 969c1602e6 | |||
| 2d4d3b18dd | |||
| 5e1d6a3691 | |||
| 533cfc0381 | |||
| 2b23712dc3 | |||
| 88275da5e8 | |||
| bd7a5ad6f0 | |||
| 1f6f82dbcf | |||
| 1f8939937a | |||
| b3d41a5f96 | |||
| fec2d493a9 | |||
| 86ee75f63f | |||
| 31941918cf | |||
| 19a65d2bea | |||
| 819d4b2b83 | |||
| b87c113cf4 | |||
| b25182971f | |||
| 1ee2c47e37 | |||
| 2dc563f1f1 | |||
| 15ba71a275 | |||
| e5b3fc49d6 | |||
| ae1766951d | |||
| 02d08dafd9 | |||
| 13a5090695 | |||
| 8e32e4c04c | |||
| cf991310c3 | |||
| 938706099e | |||
| 3330287dc7 | |||
| 38c8520adf | |||
| 492e1746af | |||
| 91a8109cfd | |||
| 161490d34a | |||
| 9c302852eb | |||
| 8654fcfd60 | |||
| b3d527d9a0 | |||
| 4d495218c9 | |||
| 13a041284c | |||
| c60c1a003d | |||
| 97add1a5ea | |||
| ca02930e47 | |||
| 20d5e95077 | |||
| eb4a7dc11d | |||
| f722498b72 | |||
| aadfb6fe83 | |||
| 6c273594c9 | |||
| e475c82fa1 | |||
| 0c2e6665df | |||
| 6295e6e94b | |||
| 670a4aa708 | |||
| 1bdc2e64ed | |||
| c587be1e50 | |||
| bd481596f5 | |||
| a504d56b43 | |||
| 91c4dfccea | |||
| 27f618c44d | |||
| a14482a1df | |||
| aa50c5734b | |||
| 293001a4fe | |||
| 638cfdf150 | |||
| 5f80a14525 | |||
| 1342fd3975 | |||
| 8d4af38489 | |||
| 575a064e66 | |||
| 3ab21a3c4f | |||
| 2f592e6c7d | |||
| 5661ffb766 | |||
| 9b74503daa | |||
| 24848f1cd8 | |||
| a31a07ede9 | |||
| c8c4c9b23d | |||
| e1ed9303f0 | |||
| a43aab13c2 | |||
| c698b4a45e | |||
| c6a0ffab50 | |||
| 8ba7cc30d1 | |||
| 61bf08ca24 | |||
| 6ada3c0c16 | |||
| 60061fbe79 | |||
| 46e7042add | |||
| d0c182773b | |||
| b6f60585b5 | |||
| 4b0e3ee219 | |||
| 838842d4b2 | |||
| e71cf20192 | |||
| adb4cb2b5b | |||
| 478d7446ef | |||
| df68230351 | |||
| 6073f9b46c | |||
| 8e8022b735 | |||
| da82d2dd70 | |||
| 82176473a5 | |||
| 240372a991 | |||
| 5b10411c8c | |||
| 4c474a9939 | |||
| 7ea6ae57c8 | |||
| 42633f8986 | |||
| 84248690a9 | |||
| 53409ca0fb | |||
| c2c1710047 | |||
| 876202503f | |||
| 946a7d9bc3 | |||
| 608bcd3b15 | |||
| 632b02a477 | |||
| 0db9c63300 | |||
| 873ed4e6b6 | |||
| 01bd43037d | |||
| 68c9e3f232 | |||
| a25c8555eb | |||
| d6ca3820aa | |||
| dfd1dff383 | |||
| 8f391d4d51 | |||
| 2a6b7685ae | |||
| eb9573107d | |||
| ee43cd7adc | |||
| 4ca26fbc1b | |||
| c165226325 | |||
| 0722775ca3 | |||
| 49295ebe54 | |||
| 455038e470 | |||
| ca7f02ea0c | |||
| 04aba1caec | |||
| 420488349f | |||
| 1a5cae7340 | |||
| c26b9c0a5e | |||
| aaf41c61a6 | |||
| dd844f741b | |||
| 4dd19988c3 | |||
| 7117a9012e | |||
| 1bdc28161a | |||
| 5e150caf38 | |||
| c0c62d099a | |||
| b9ece39685 | |||
| b14d6318f8 | |||
| 93002720eb | |||
| 7c44506441 | |||
| 937ba581d7 | |||
| 2ae54f1194 | |||
| cb91078e01 | |||
| a217fefee1 | |||
| 34b7fed802 | |||
| 5221745c21 | |||
| 000ca44b16 | |||
| 8f3d44033b | |||
| 7cc14c595a | |||
| 797544c47a | |||
| 336eeee895 | |||
| 593f867e3e | |||
| 385913be1c | |||
| 6aaa14f5fe | |||
| 07f5b21ef1 | |||
| ee52f89772 | |||
| e454870396 | |||
| 2822013437 | |||
| 72c1982734 | |||
| 0de2ea305a | |||
| d899385a3d | |||
| c6d6cbe8a6 | |||
| 85e82e85d8 | |||
| a1534cc37d | |||
| 8c8dc791ef | |||
| 63edca44f2 | |||
| 6aa8c932fc | |||
| 8821f4aba6 | |||
| 5e06634f7e | |||
| b82c4b3d38 | |||
| 8d90ab2d9b | |||
| bd5303010d | |||
| 16d2c3d7b3 | |||
| 407a92dc26 | |||
| 0a893abc7b | |||
| 34fa5e0dc7 | |||
| 712686ce91 | |||
| 518864a7e0 | |||
| 72fd605b01 | |||
| 750fb5cc73 | |||
| 0f4749907a | |||
| bd2dc63ef6 | |||
| 19a8795450 | |||
| d9dccfdd71 | |||
| 7547a06c4f | |||
| 8929b75795 | |||
| 4d37ef878c | |||
| efd8998690 | |||
| 126e77d5c6 | |||
| 53eec78bea | |||
| a4edaec81a | |||
| 92481b59d3 | |||
| 6c77fa9121 | |||
| aeb7a72620 | |||
| 73d232ee45 | |||
| c0c65bf915 | |||
| f6cee952af | |||
| e74184f679 | |||
| 3884d36176 | |||
| e7c6886a00 | |||
| 024d1e2678 | |||
| ed8e92f63d | |||
| fb97df5d65 | |||
| e9b05c71b4 | |||
| 5eab428294 | |||
| 7926324385 | |||
| 1527b37c26 | |||
| de4659659b | |||
| a96a8c8336 | |||
| 691aa19b88 | |||
| 6b07dc9e22 | |||
| 91a17b702b | |||
| c54597e0b2 | |||
| a9785bba44 | |||
| 833b8cbc7a | |||
| 75aeb16e05 | |||
| fc354a0d6e | |||
| 262611fcd3 | |||
| b8a34f3033 | |||
| 10bb6bb9b8 | |||
| 3c9ef69c37 | |||
| dee987d6ee | |||
| 138f254ec1 | |||
| c7c8aaa7f0 | |||
| d0db624e02 | |||
| e3e7b76310 | |||
| dad02bceb9 | |||
| b195285879 | |||
| 8f3da5b51d | |||
| 825e919eb8 | |||
| acb0ce8885 | |||
| 72089c9c36 | |||
| cf2f158fec | |||
| e4886f6589 | |||
| 6470b5bd21 | |||
| 44196955e2 | |||
| f08ec1394d | |||
| f8fb25e0a2 | |||
| 6a0c66752f | |||
| a1bd4efb08 | |||
| b43ce05268 | |||
| 80e56cfda9 | |||
| 24701fc5a7 | |||
| f78a266d99 | |||
| f096fb6859 | |||
| a3e11d606b | |||
| 79232c24e2 | |||
| 15d9d499ab | |||
| 962084c8e8 | |||
| 7518b1eefb | |||
| 8215d7a4ba | |||
| 5aaa220d84 | |||
| 12c16ab9bc | |||
| 76520512e7 | |||
| 66de965882 | |||
| 10d32fb0b7 | |||
| e72c9b6e4a | |||
| ac1f68127a | |||
| 60d1852c7b | |||
| d53eb521fc | |||
| 9808932f10 | |||
| ea876eb6d5 | |||
| 0a45864866 | |||
| 2560b39796 | |||
| 21afa4c88b | |||
| 9fc3c5e4d2 | |||
| 3e3501c98d | |||
| 5e6fcd02b5 | |||
| d46ebcfadf | |||
| 41480c8cf2 | |||
| 236890d902 | |||
| 55632d81d2 | |||
| 0b276d622e | |||
| c81491b37d | |||
| 42e189425f | |||
| 3cfa0d7199 | |||
| 7c9e088661 | |||
| e78aa4bb84 | |||
| f8e94d0d8b | |||
| ebe6f40fce | |||
| 5fb37efb46 | |||
| 4f47855873 | |||
| 52ae6f682f | |||
| c35f58f97b | |||
| 659b2f3154 | |||
| 5ea05cfb96 | |||
| dc9a5b7d2f | |||
| f7ab5a128a | |||
| 368cbe615d | |||
| d4c9a3782b | |||
| 172dca5e8b | |||
| 818bf0c408 | |||
| 03dcf8a83b | |||
| 604f607fd1 | |||
| 956d946c25 | |||
| 970caaa621 | |||
| 00a5980cdf | |||
| e24eee04f0 | |||
| f1b3af4ee2 | |||
| fb2d28f477 | |||
| 3a704ff725 | |||
| 0180e638e5 | |||
| 95c6ae04fb | |||
| 27c4c6e0af | |||
| da17414b3f | |||
| be2b27a747 | |||
| aec2c8f752 | |||
| 13e34b4679 | |||
| 57373c7c29 | |||
| 79f5bf84e5 | |||
| 3ed720079e | |||
| e7c1e6a8e3 | |||
| f1d0d73ed7 | |||
| 9c411513bf | |||
| ce78bc898b | |||
| 887002e932 | |||
| 31dea5ff23 | |||
| ec4602a973 | |||
| a38749d15f | |||
| 6ee77b4edd | |||
| 6328981fcf | |||
| a90913105c | |||
| 9368596059 | |||
| 80ed795ff1 | |||
| a2938e3d11 | |||
| 2ad967dbe4 | |||
| 7415c090ac | |||
| a1fa995044 | |||
| 3c2ecc6b15 | |||
| fa1516d319 | |||
| b5ebf68df1 | |||
| aa46055274 | |||
| 2cad802b68 | |||
| 2d01f384f1 | |||
| f8d4f980b3 | |||
| 4f5a6c366e | |||
| ecfcf39f30 | |||
| 3975a2676e | |||
| 138ee75a3b | |||
| 0048f228cb | |||
| 2748b920ab | |||
| a92a2312d4 | |||
| 945ce5cdb0 | |||
| b39de2cbbe | |||
| ce13900148 | |||
| 4c77ad6ee4 | |||
| 0bc4246425 | |||
| c45ff2efe6 | |||
| 99b520cc5d | |||
| e05607aee1 | |||
| a360ba1734 | |||
| c661b963b9 | |||
| e374dc1696 | |||
| 116e0c7f38 | |||
| 45596d5289 | |||
| 342e7b873d | |||
| 00410c4496 | |||
| 8b9276bbee | |||
| 3238786ea1 | |||
| 07ebbcbcb3 | |||
| ca555abcf9 | |||
| 63893c3fa2 | |||
| f8ae34706e | |||
| f8e89fbe11 | |||
| 30d208010c | |||
| 017c7efb43 | |||
| 0c69fd559a | |||
| c991258b93 | |||
| 9f89692dcd | |||
| c28575a4eb | |||
| c9db9c2317 | |||
| 16a09304b4 | |||
| 58a88d1ac0 | |||
| b740878697 | |||
| 7179002bfb | |||
| 173c81c2d2 | |||
| ee4c77c59f | |||
| 30ec12fdd5 | |||
| 269ec0566f | |||
| a0a95c95d4 | |||
| 1335b7c1da | |||
| 6d14ef8083 | |||
| 26a492acf3 | |||
| f2741e8038 | |||
| 8d1a6975d2 | |||
| c414bf0aaf | |||
| 99f4864674 | |||
| 784cbeff5b | |||
| 9302f860ae | |||
| ac8a5e7f0d | |||
| 798fc16bbf | |||
| 0f65c9267d | |||
| be45231ccb | |||
| 279aea683b | |||
| 8aa8f791fc | |||
| 6464e69e21 | |||
| a93812e4e5 | |||
| 225f942044 | |||
| d951d5b1cd | |||
| 2082ccbf59 | |||
| 473e795277 | |||
| a09f653f52 | |||
| 90fe6dd528 | |||
| 57a2ccf777 | |||
| 205b9bc05f | |||
| 14d5d52789 | |||
| 9c218b419f | |||
| a69d819901 | |||
| 517fb2f410 | |||
| fef2b1526d | |||
| 3719994c96 | |||
| 35c2821d71 | |||
| e4812b3903 | |||
| 4cc11066b2 | |||
| 85b64d77b7 | |||
| db7948d7d5 | |||
| 3d40c0562d | |||
| 146bcc0e70 | |||
| 8d9f6c2583 | |||
| ac32d8b706 | |||
| 15c1dad340 | |||
| 6d8baf7c30 | |||
| 7ced682ff5 | |||
| 89cab4f5e6 | |||
| a0afb79898 | |||
| d6fa3b3fd5 | |||
| f91bb96071 | |||
| 3b6644d195 | |||
| 652b468ec2 | |||
| af110d37f2 | |||
| 38967568ca | |||
| df79631a72 | |||
| 95f0fa8a92 | |||
| 1c6ff53b60 | |||
| 1dbf44c00d | |||
| 1259a0648b | |||
| b0055f6229 | |||
| 90040afc44 | |||
| 59bc96bdc2 | |||
| 676ffee542 | |||
| 77136e4c13 | |||
| 604e13775f | |||
| 02380a74e3 | |||
| 4461ae8090 | |||
| 2b948c42cd | |||
| 133c1e927f | |||
| 2290798a83 | |||
| fd600b11a6 | |||
| b5c9f5c4c3 | |||
| b8a5b1ed8e | |||
| ca74bb17b8 | |||
| 69d8331195 | |||
| eab5c1975c | |||
| e67b525388 | |||
| 5171e56b82 | |||
| f467848448 | |||
| 7e4ddcfe8a | |||
| 3152be5fb3 | |||
| b076944dc5 | |||
| 3a07228509 | |||
| 24a2f2e3a0 | |||
| b32dd4a876 | |||
| 4f4bd81228 | |||
| 59b23d79c6 | |||
| 8c14630e35 | |||
| cc32de8ef9 | |||
| 44696c1375 | |||
| 82088a8110 | |||
| d5e45b2278 | |||
| bdfef2975c | |||
| b4bb4b64a1 | |||
| 3e91c5e1ad | |||
| 2b88d85505 | |||
| 50651970b8 | |||
| 4a8906dd8a | |||
| 68e2769a13 | |||
| 17c998e99a | |||
| 35758f51f2 | |||
| e8102b0a9b | |||
| 04f2bc9aa7 | |||
| d070178dd3 | |||
| c9ec7fad52 | |||
| f0a6ca4d53 | |||
| fd92470e23 | |||
| 8369664445 | |||
| 35e1adfe82 | |||
| eb91fc5e5d | |||
| d186fdb34c | |||
| 0f04f71b7e | |||
| 87f1959be7 | |||
| a538055e81 | |||
| 0e345aaf6d | |||
| c976dd339d | |||
| 71cef62436 | |||
| 3a29055044 | |||
| 59d66e6963 | |||
| 46bc43a80f | |||
| 7fa60b2e44 | |||
| c78893f912 | |||
| 0d2a4e1a9e | |||
| 088f14c697 | |||
| 4bf7be7bd5 | |||
| b2ab6891c5 | |||
| 39ab5bcba8 | |||
| 42f131c09f | |||
| 89dca6ffdc | |||
| b7f36f93d5 | |||
| 58320d5082 | |||
| a461804a65 | |||
| 817f6cc59d | |||
| 108936169c | |||
| f60ae085e6 | |||
| 85dda09f95 | |||
| 4f479a98d4 | |||
| 35ba948dde | |||
| 6b4ed52f10 | |||
| dcf5f8671c | |||
| 5340291add | |||
| 1c6fe58574 | |||
| 9f2111af73 | |||
| 2ed6c6d479 | |||
| 01ac2d3791 | |||
| eac687df5a | |||
| 6a2785aef7 | |||
| 849cbf3a47 | |||
| a0c614ece3 | |||
| 1b97f088cb | |||
| 097399cdeb | |||
| 7ee152881e | |||
| 3074f8eb81 | |||
| 748208775f | |||
| 5df17050bf | |||
| 92df0eb2bf | |||
| 995195935b | |||
| be8376eb88 | |||
| b650a45b9c | |||
| 8a20e22239 | |||
| 7c5014d803 | |||
| 62ac1b4bdd | |||
| 0633c08ec9 | |||
| cf87cc9214 | |||
| f908432eb3 | |||
| 1bd291c57c | |||
| b277df6705 | |||
| ec4d597c59 | |||
| d2ef49384e | |||
| b5dc36f278 | |||
| 41976e2b60 | |||
| 3dac1b9936 | |||
| d2bb56647f | |||
| 224422eed6 | |||
| 3c26f7a205 | |||
| 9ac9809f27 | |||
| 7bf6e984ef | |||
| 10f78985e7 | |||
| dc95f66a95 | |||
| 47f56f0230 | |||
| b4018c4c30 | |||
| 43fbdd3b45 | |||
| 9d2d884313 | |||
| c0600e655a | |||
| 671ed89f2a | |||
| e0372643e1 | |||
| b5cf1d2fc7 | |||
| 52c2a92013 | |||
| 541ab961d8 | |||
| 849794cd2c | |||
| f47fa2cb04 | |||
| 7a162dd97a | |||
| b123bace1b | |||
| 483490cc25 | |||
| 8d60e39fdc | |||
| e7dff91cf3 | |||
| ab5776449c | |||
| a229582238 | |||
| a0df8fde62 | |||
| e4a3aa9295 | |||
| be98c5d12d | |||
| bc6a71b1f5 | |||
| 26f1e2ca9c | |||
| 75d850cfd2 | |||
| f4870ca5c6 | |||
| 235d5400e1 | |||
| 491d5ba4fd | |||
| d42eadfeb9 | |||
| 9a40821069 | |||
| 2975f539ff | |||
| 64ca584199 | |||
| 5263469e21 | |||
| c367e0b64e | |||
| 183b3aacd2 | |||
| 101950ce92 | |||
| 239ae94389 | |||
| 55e850d825 | |||
| 62af45d99f | |||
| 1ac038ab24 | |||
| 77a925ab66 | |||
| d0d33d3ae7 | |||
| 9b7eceddc8 | |||
| 24af02154c | |||
| 86ec14e594 | |||
| 8a29338837 | |||
| 29918c6ca5 | |||
| 80a44e84dc | |||
| 5497b1babb | |||
| bef70aa377 | |||
| 0d30f77889 | |||
| e27bb3e993 | |||
| 179d5efc81 | |||
| b55e38801d | |||
| e704ec5c6f | |||
| 6cda6bb34c | |||
| 46f0248466 | |||
| 310ec57fd7 | |||
| cd82b2b869 | |||
| 126a1cc398 | |||
| bf650f05b3 | |||
| f2606a7502 | |||
| b07fe52ee0 | |||
| b07358b329 | |||
| 2aea8077f9 | |||
| 41f9c14297 | |||
| 135687f04a | |||
| b140e70b58 | |||
| ec987b57f6 | |||
| 596677232c | |||
| 9d74e139e5 | |||
| bc475cad67 | |||
| 45d6212fd2 | |||
| f45d75ed22 | |||
| b03407289f | |||
| 93ed476e7d | |||
| 10faa303bc | |||
| 6fa371cb0d | |||
| 18a2691b4b | |||
| f7bd3f7932 | |||
| f8dee4620a | |||
| 800e24616a | |||
| d63a435787 | |||
| a9c2809ce3 | |||
| fa61159dd0 | |||
| a215e000e9 | |||
| f16a624b35 | |||
| 61c2896cb8 | |||
| 22ebc3f205 | |||
| 8fa9f443ec | |||
| bb72ccf1a5 | |||
| 2e73456f5c | |||
| 3e49a2b4b7 | |||
| 4694e4050b | |||
| 59b9eeff49 | |||
| 1744fad8c2 | |||
| e46d942ca6 | |||
| 93a6136863 | |||
| 230bde94e7 | |||
| 20fffc8bb7 | |||
| 861a3f3a30 | |||
| ee52102943 | |||
| 26516f667e | |||
| 5586f48ad5 | |||
| cc6e3c92d2 | |||
| a2ef5782d0 | |||
| 0c1c0e21b8 | |||
| ffcc38cf05 | |||
| cc24b68584 | |||
| 8a70067b92 | |||
| 33b227c45b | |||
| fb68be952d | |||
| f413ee087d | |||
| 6495f5dd30 | |||
| 8e09f0590b | |||
| 08d346df9c | |||
| 12cf96e358 | |||
| 765a720d1c | |||
| cace62f94c | |||
| 767c96850d | |||
| b73e78edbb | |||
| 7914cc119d | |||
| 2b13eb2a6c | |||
| 8768e64e97 | |||
| 9212b9ca09 | |||
| 0d0f197682 | |||
| 281e34d1b7 | |||
| 287ba38905 | |||
| ed9dbff4e0 | |||
| 6ba4e48521 | |||
| b7269f2295 | |||
| 5ab317d4a6 | |||
| 431bcf7afa | |||
| 41909e8c5b | |||
| 56245426eb | |||
| 3adcb2c157 | |||
| 6d12185cc9 | |||
| 258c9ffb2c | |||
| dede431dd9 | |||
| 6312d29d80 | |||
| ab5f26545b | |||
| 6567c1342d | |||
| 3d6c2e023c | |||
| 89d930335b | |||
| 04393cd47d | |||
| 28f0cf6cee | |||
| 1af9a9637f | |||
| 1031d671fb | |||
| 2a974f5ca2 | |||
| ee91b22317 | |||
| 648e9fbb58 | |||
| 9f7114a4a1 | |||
| 7d03da0890 | |||
| 4e0cecae7f | |||
| 72dbb76a15 | |||
| 0d7d29fa57 | |||
| be3276fcdd | |||
| 09c94a170c | |||
| f2a18004a7 | |||
| 1a3ff1bd28 | |||
| a5d3c779c7 | |||
| 9d32e60dc2 | |||
| 34d27771c6 | |||
| 1093821c33 | |||
| 91f2946310 | |||
| 2bd7a3c31d | |||
| a681f6759b | |||
| cb849524f3 | |||
| 1f5951693a | |||
| 87748ffd4c | |||
| 0580f5a928 | |||
| 88d9fdec2e | |||
| 506a40ce44 | |||
| bf0e185bd6 | |||
| 5b3ccec10d | |||
| eb07581502 | |||
| 934a2b6878 | |||
| bec6ab47b6 | |||
| 49480f1548 | |||
| 18a3c62d9b | |||
| 6322cf3234 | |||
| 4e2b154342 | |||
| bb1019d1ec | |||
| 162170fd7b | |||
| ea728e7c5e | |||
| aea6ba4bcd | |||
| 606aa43da0 | |||
| 8bfa802665 | |||
| ddddfba1c0 | |||
| 86c95014a4 | |||
| 288c950c5e | |||
| b27d4de850 | |||
| 61063ebade | |||
| 3e70e26278 | |||
| 66e7e42800 | |||
| 0fecec14b8 | |||
| a7f24ccb76 | |||
| 04e896a4b4 | |||
| 5dcfb80b36 | |||
| 9da60c39ce | |||
| 379860e457 | |||
| bcfa2d6c79 | |||
| 8b492bbc47 | |||
| a49b7b0f58 | |||
| c781ac414a | |||
| 656dca6edb | |||
| 830adfd151 | |||
| 6f7c8e4ef8 | |||
| 5765d608cc | |||
| 2ba6678766 | |||
| 71a47d1bed | |||
| 51bf6321ea | |||
| aa8916e7c6 | |||
| 2e24da2a0b | |||
| c94ccafb61 | |||
| 80a827d3da | |||
| c07105a796 | |||
| c40c061a9f | |||
| a9bd27ce5c | |||
| 2e36c4ea2d | |||
| 4e45385a8d | |||
| cf5e925c10 | |||
| 709255d995 | |||
| e3f440b1d0 | |||
| f6b94dd830 | |||
| 3911a1d395 | |||
| ebd3648fd6 | |||
| f698f09cb7 | |||
| 86aa5dae05 | |||
| 179c82ffb4 | |||
| 233017f01f | |||
| c2c515516b | |||
| 9c18468fe2 | |||
| 597bbfeacd | |||
| 0613ac90cd | |||
| 78871d829a | |||
| d40a7bf9eb | |||
| b27f576f29 | |||
| 073dfd8b88 | |||
| 509dd57c2e | |||
| 7a837b7a14 | |||
| dee864116a | |||
| 5f2b32e45b | |||
| e51d0bef97 | |||
| 2fd78112ab | |||
| 5c14bd2888 | |||
| 84b4665e02 | |||
| 26d626a47c | |||
| 6ff6299c65 | |||
| 071e68d99d | |||
| 78c1094d93 | |||
| 56fc639c9f | |||
| 51084a9054 | |||
| f8ae5c93e9 | |||
| ad286c0692 | |||
| a483b3903d | |||
| 6564d39777 | |||
| 8f1b7230fe | |||
| c0b7608965 | |||
| 56dd4132c4 | |||
| 9057eade95 | |||
| a28317b263 | |||
| 25c3603266 | |||
| ae6f2dd11c | |||
| 3aaa1771d5 | |||
| 2034396a3c | |||
| 0cad668065 | |||
| f644a11b82 | |||
| d7e3b2ef29 | |||
| fc5ec87478 | |||
| ed4023127b | |||
| 2bd4e5f5f6 | |||
| d2dcbc26f8 | |||
| 2f05eefe9a | |||
| 7d1afa78b9 | |||
| dac9b020e0 | |||
| eb77b79df9 | |||
| 456998f043 | |||
| c09f07edd9 | |||
| aeed8a6ea4 | |||
| c82537462b | |||
| a8a02ff560 | |||
| 72a9df19c8 | |||
| 5b9b9634f9 | |||
| c279a91c03 | |||
| ef6a764509 | |||
| 4db5afdf7e | |||
| 7867187451 | |||
| 4f8e6ec42a | |||
| 64c8a13773 | |||
| 395ab4a287 | |||
| 15dc862056 | |||
| f2daa616d1 | |||
| 64a50f5ad3 | |||
| 1d0f86144c | |||
| 89e93bba9d | |||
| 3290d4c7d6 | |||
| ca22befc93 | |||
| b08df5b9c0 | |||
| ebd3c3291c | |||
| 16728d2f26 | |||
| 34dab66f44 | |||
| 3a111c7499 | |||
| 3600c94ec5 | |||
| e2f8b00e00 | |||
| 65ed1eba48 | |||
| 7fff7977fe | |||
| add5922aac | |||
| a94b54a533 | |||
| bea82b9da6 | |||
| 2e7debe282 | |||
| d57e1a6756 | |||
| c9172c5bc9 | |||
| 5d5e877a05 | |||
| 1e794c87ae | |||
| d9cb1b545a | |||
| 23f611f14d | |||
| 42b28d0d69 | |||
| d0cf5f7b65 | |||
| 4699c817e8 | |||
| 4f490c16e9 | |||
| bcdab7a632 | |||
| 7f51af7cbc | |||
| b4ae60cac8 | |||
| 4d03d96e8b | |||
| a39ffebc3a | |||
| 4bba6082ed | |||
| b111632965 | |||
| 0a34b34bfe | |||
| 6b821ece22 | |||
| d3b2096bfd | |||
| e64fca4b04 | |||
| b941e73f4f | |||
| c57873d3cb | |||
| f3bc3275ac | |||
| 8df26e6c5c | |||
| 5c8ecb8150 | |||
| 3928f7740a | |||
| 1767f73e6b | |||
| 9e7d5e93ab | |||
| 70c6ee93a2 | |||
| 5cbf8504ef | |||
| 9a393b023d | |||
| 30bf464f73 | |||
| 9fb1f8934b | |||
| f3f02b23a0 | |||
| 7668cdd32c | |||
| f9dafdcf09 | |||
| d284a419c1 | |||
| b45844e3d9 | |||
| 6caa7e0fff | |||
| 1669fffb8d | |||
| 18aa86eebd | |||
| 075e49d3f4 | |||
| a6695b8365 | |||
| 06ee48b391 | |||
| fcaeffbbd4 | |||
| 6146a9a641 | |||
| 83de8e40d5 | |||
| 30590c46a3 | |||
| a3a5e56287 | |||
| 185c96d63a | |||
| be61ad6eb4 | |||
| 222dfd2259 | |||
| b06e1c7e1d | |||
| 6876abba51 | |||
| 0798466a01 | |||
| 2cda782273 | |||
| 7d1c9554b6 | |||
| a29d16f1a8 | |||
| 6d0c1c0f17 | |||
| 5ed4b5c25b | |||
| 6fe89c5e44 | |||
| fda8c37641 | |||
| 6d5a0ff3a1 | |||
| f8718dd355 | |||
| 85af686797 | |||
| 0f6ec3f15f | |||
| 44644c50ee | |||
| 9749f7eacc | |||
| d9a2bdb9df | |||
| 57e678c94b | |||
| 516f127cfd | |||
| e477add103 | |||
| ba3d577875 | |||
| 917e4f47c4 | |||
| 0143dac247 | |||
| d2390f3616 | |||
| 949ea73402 | |||
| d1e2fe0efe | |||
| 584ada12bf | |||
| 3ead72f654 | |||
| 9ce96d3bd3 | |||
| 5549c003d9 | |||
| 46105bf90b | |||
| 73ce3b3702 | |||
| 1c6225dc2f | |||
| 44874542c8 | |||
| 31f2846aff | |||
| bc08011e72 | |||
| 7cccc216d0 | |||
| 09493603f6 | |||
| e799bd0ba9 | |||
| 40247b0382 | |||
| cd2e9c5119 | |||
| 0b6f7b12b1 | |||
| 86e42ba291 | |||
| 8c2f77cab6 | |||
| c1bd6ba1e1 | |||
| df59b89fbb | |||
| 8fd9cc160c | |||
| 28e3f07b63 | |||
| 513d902df1 | |||
| fce14a9f51 | |||
| 884107da01 | |||
| caa79a354a | |||
| 5bb873a2fe | |||
| bc0442d7df | |||
| cfcd33552b | |||
| 5f6b9fd5ba | |||
| 469dce4a2d | |||
| 55d32de331 | |||
| 4491d2d3cb | |||
| f9669b9b9a | |||
| 246d5f37c7 | |||
| 293bfb03dd | |||
| 4def4e696b | |||
| b6e58c030a | |||
| bf00308ab2 | |||
| e3e786e35e | |||
| fd67794574 | |||
| 0676cad200 | |||
| 3b1d217310 | |||
| 93bcb2e7ba | |||
| ebc70f7919 | |||
| e32af0196e | |||
| 3e5c121c56 | |||
| e644f6ed2c | |||
| 551a7c72f3 | |||
| 05b121841e | |||
| c29aea89ee | |||
| 103e70ccc5 | |||
| ec7ecbe2dd | |||
| 7a06dbb87e | |||
| 1234e434fa | |||
| 2d374f982e | |||
| 4e73630a95 | |||
| e867baa5f9 | |||
| 04b750cb52 | |||
| 97c7b12542 | |||
| 0dfec752a3 | |||
| f16f68e103 | |||
| 4b7f8f9b77 | |||
| 9969d50833 | |||
| 7355c63845 | |||
| 16cac6442a | |||
| 5009ae5548 | |||
| 32647e285e | |||
| 6df334ea68 | |||
| f8501042c1 | |||
| be085b8f6c | |||
| ef557761dd | |||
| 15377ac391 | |||
| ad5fdef6ac | |||
| 0cb5943be8 | |||
| fb593d5f28 | |||
| 645c913e4f | |||
| b4f4cca875 | |||
| 6027513574 | |||
| 849188fdab | |||
| a9c14a5306 | |||
| 2da36a14d1 | |||
| 2ee451f5f7 | |||
| f2d7e94948 | |||
| 2031dfc08a | |||
| 34ede14877 | |||
| 2af3098e5a | |||
| 2e44511b13 | |||
| 7bc4aa7e72 | |||
| e2458bce97 | |||
| ae9789fccc | |||
| 45ef25ea27 | |||
| ad2d413c0b | |||
| 30924ff1e0 | |||
| 383c48968f | |||
| bbe8627a3f | |||
| 2bd36604e2 | |||
| 9ed47ef531 | |||
| 139f98a872 | |||
| c825895190 | |||
| 42e835ebb8 | |||
| 5505e1de7d | |||
| 6d329e418b | |||
| 3a11afb57f | |||
| df86e02c9e | |||
| deebc1383e | |||
| 19f2f1a9d3 | |||
| 4dc13ecdd8 | |||
| b4b6e356ef | |||
| 9000f40e61 | |||
| f137c0c05a | |||
| b43a02a9aa | |||
| 30be715900 | |||
| 71cf8e14cb | |||
| ffd4863b23 | |||
| 4c17098bb8 | |||
| bcfdd18599 | |||
| 067662d280 | |||
| 93d02e4686 | |||
| 12de115305 | |||
| b5d13296c6 | |||
| 86288265ad | |||
| a559d94a44 | |||
| 1eb6870853 | |||
| f88c3e9c12 | |||
| 942ca477a6 | |||
| b0e33fb473 | |||
| d58b627b98 | |||
| b85fc35f9a | |||
| bcb466fb76 | |||
| 6db721b5dd | |||
| 140c65e52b | |||
| 29e8d77ce0 | |||
| 4d0d775d16 | |||
| 98f67e90d5 | |||
| fee67c2e1a | |||
| c295f26a00 | |||
| 8a09c45f28 | |||
| 79ead42ade | |||
| 94e52e1d17 | |||
| 3931beee81 | |||
| d293c17d21 | |||
| 1a3920e5dc | |||
| ffc3eb1a24 | |||
| 2f5d4a7318 | |||
| 70553f4253 | |||
| 8d39fb4094 | |||
| 7d10b2370f | |||
| 31ec7650ac | |||
| c014920dc1 | |||
| 17e3d4e1ee | |||
| b01c785805 | |||
| 0eea71f878 | |||
| 429f2d6765 | |||
| a0c7e3cf04 | |||
| 9cd68129da | |||
| 6fa9c87aa4 | |||
| ee14cf9438 | |||
| 0391bbb376 | |||
| 28ada0c634 | |||
| 2c233d23ad | |||
| 59c628803a | |||
| 6b830bc77f | |||
| f30081a313 | |||
| c15648c6b5 | |||
| a02917f502 | |||
| 70d8bd04c0 | |||
| ad2cee0cae | |||
| 756a7122ad | |||
| 3d6ebde756 | |||
| daa30aa992 | |||
| 39459eb238 | |||
| 0325e2f646 | |||
| 93b8b5631f | |||
| 60ab1ce0c1 | |||
| 2f186df52d | |||
| 452e07d432 | |||
| 05d1404b9c | |||
| 534b9a1697 | |||
| b2781d0501 | |||
| bf7d1514f7 | |||
| 2acee24332 | |||
| e7639e55f8 | |||
| eb3ac2b367 | |||
| 968d386b36 | |||
| 38cb3d0227 | |||
| 6f606dd5f9 | |||
| bab616cf11 | |||
| 966adc6291 | |||
| 518cb6ec7c | |||
| 34bcd4c237 | |||
| a121127082 | |||
| 50326e94b1 | |||
| 160723b5b4 | |||
| 7991125293 | |||
| 96f61bff30 | |||
| a94488f584 | |||
| f2cf673d3a | |||
| c4595a3dd6 | |||
| 8bb06c94be | |||
| 1620c56808 | |||
| e88e0026b1 | |||
| ace9b49e28 | |||
| da90751add | |||
| 8cc566f7b5 | |||
| 02ad199905 | |||
| c3e0811d86 | |||
| 499d1c5709 | |||
| cf16ec45e1 | |||
| daa15dcceb | |||
| 32556cbe5e | |||
| 74d9c674f5 | |||
| a4da558fa0 | |||
| dba6d1d57f | |||
| b01c4338c9 | |||
| 811d947da3 | |||
| de7bf7efe6 | |||
| 5537df9927 | |||
| 81fea93741 | |||
| df1065a2d8 | |||
| c2e3bf2145 | |||
| a4d849ef68 | |||
| 957c9f3853 | |||
| 3958b6b0e1 | |||
| 5d70feb573 | |||
| a22af69335 | |||
| 1213149a2f | |||
| 398b6f75cd | |||
| e46e05e7c5 | |||
| 166028836d | |||
| 3cbe66ba8c | |||
| 99de537a2e | |||
| 1d0afdf9f7 | |||
| 4db6667923 | |||
| 80e16e44aa | |||
| 58b134b793 | |||
| 6efefac2df | |||
| 0c9670ddf0 | |||
| 53c65ddc6a | |||
| 33371c5164 | |||
| 64dd1419c5 | |||
| 108068a417 | |||
| 6e8ed95ada | |||
| 39c9f9e9e8 | |||
| b555588f5d | |||
| 47ef4bb0a0 | |||
| b34654bf97 | |||
| 6068df3ab2 | |||
| bb35999f51 | |||
| 25c51c49aa | |||
| 833bedb46b | |||
| 3d8eba7b42 | |||
| ab0e86ae4b | |||
| 94b35312d0 | |||
| f4ebc65a12 | |||
| 2bc9da4f5e | |||
| e034f258e3 | |||
| 39adf6dbd2 | |||
| 112df5f664 | |||
| 3564b77553 | |||
| c813e93d85 | |||
| ff59385034 | |||
| ea4f812a12 | |||
| dbe540e49f | |||
| c1c0969834 | |||
| b87f26ce26 | |||
| 67335e638c | |||
| 90916f34a7 | |||
| 11b38a6895 | |||
| a1f5fe6a8f | |||
| 5cad164dee | |||
| 7dd28b885d | |||
| c20828478e | |||
| 3e1c88e3e0 | |||
| e98a4ea336 | |||
| e8a5f00866 | |||
| d92b7da733 | |||
| 7ff16baa7d | |||
| 93e60715af | |||
| 14965cfce9 | |||
| da1e3f084d | |||
| 0b0a62420c | |||
| c92c82aa1a | |||
| 4742c08c7c | |||
| 9c6ced1c0a | |||
| a33c9bd774 | |||
| c8a4734b97 | |||
| 3f7ab95890 | |||
| 2d8c2972ae | |||
| 941cf4e63d | |||
| 57610a7471 | |||
| f5a6a3b0e9 | |||
| bab7f89cdc | |||
| cb5d4e836f | |||
| 3a5544f060 | |||
| 412019dbe4 | |||
| f9d9c92560 | |||
| 7f4ff0e615 | |||
| 3eac7164f4 | |||
| f9d25e8e72 | |||
| 52ed57352a | |||
| 1828e7c42f | |||
| 2c89ae4e8a | |||
| 779a460030 | |||
| 0312f939d6 | |||
| 89666fc4fe | |||
| 44527ab5be | |||
| a0cf6658c5 | |||
| 5107f23126 | |||
| 4a5557203b | |||
| c020a8502b | |||
| 44481354fc | |||
| 974fb1b09a | |||
| 4e9f0a8255 | |||
| fa1f286cae | |||
| 85bd287b7b | |||
| 0eff3897e3 | |||
| e26e35a9ee | |||
| 980300b381 | |||
| 1cf87e8a0b | |||
| 817d860af5 | |||
| 0be5031a93 | |||
| 1ed488da4f | |||
| ddf1598ef8 | |||
| 4a8a185aa4 | |||
| 4cdeae3283 | |||
| 5030d76acf | |||
| c51e2c8b8c | |||
| eec0420eb3 | |||
| e66ea56bb3 | |||
| eefa0c7b40 | |||
| a489884da4 | |||
| 7a74d3fc9e | |||
| e71204b52f | |||
| ca330b110a | |||
| 6c77476cc1 | |||
| cabd6848e4 | |||
| e3dbc6110e | |||
| 1d6715fe20 | |||
| 06ab3f962f | |||
| df77a8a81a | |||
| 94b7c32eb3 | |||
| 8fdec15a55 | |||
| e8b1217b28 | |||
| f56f06d88d | |||
| 0f7a1e27d0 | |||
| 5114d94ad9 | |||
| f74c42bf00 | |||
| a8e816f450 | |||
| a90c259eda | |||
| e223564a55 | |||
| 7847d77405 | |||
| 089d223922 | |||
| 930085ec9c | |||
| b5f7720ab9 | |||
| a0a2d9885a | |||
| 0557143801 | |||
| 64a15928d7 | |||
| d1fda539b7 | |||
| 95d545e75b | |||
| 491fbfdc8c | |||
| 5d24432322 | |||
| da5bb373e6 | |||
| 6584b35db2 | |||
| e5874ea40d | |||
| 4bad029fd4 | |||
| 7920b9229b | |||
| 9ee6189bf9 | |||
| 9b7d47935a | |||
| 19ec206bad | |||
| fb39971464 | |||
| 3ea1da3b2c | |||
| a0fb1ab86e | |||
| 60f4d285af | |||
| aa092f03d8 | |||
| f2cb3e0b7b | |||
| 1d9b10d312 | |||
| ccf7a3043f | |||
| 26a614b4d1 | |||
| 297fa957f7 | |||
| 05fb544f23 | |||
| 96cd92a0a9 | |||
| 31c45e0a08 | |||
| 65d4055366 | |||
| 1f2695e875 | |||
| 59556d0943 | |||
| c65e795435 | |||
| 9842be4b15 | |||
| eb6419f02b | |||
| a6d1f6aee4 | |||
| 73d15cf643 | |||
| a4dd9d0b86 | |||
| 49cd6f99d8 | |||
| 159cba815b | |||
| cbb76eae04 | |||
| 21a189fee6 | |||
| 4cff149c46 | |||
| 788ff68c1f | |||
| 009667c26c | |||
| d9f8f39a9a | |||
| 1fe27380f0 | |||
| 621d6a4475 | |||
| 30700ede39 | |||
| 8a76dc8b59 | |||
| f646391f26 | |||
| 1703f2abed | |||
| ee85fe1a9c | |||
| 0703e0e897 | |||
| 3d6b805652 | |||
| 58f507f9e3 | |||
| 24fe4b8af3 | |||
| 46fa7d987b | |||
| 90a7a79a19 | |||
| 64938f75b3 | |||
| 043be6f55c | |||
| cef9bf7f29 | |||
| dac5a0a07e | |||
| 76ac35cdaa | |||
| c1f0e10a59 | |||
| 1bd76a717d | |||
| cd0929aa5e | |||
| 1486d880b0 | |||
| b738b09606 | |||
| 7188f0851d | |||
| 47700a4c20 | |||
| 3aeb0f5408 | |||
| 07d1acd798 | |||
| 4cffa2219a | |||
| 11852e5c22 | |||
| eb22208a4e | |||
| bf8b4779cc | |||
| 0e7ab61a83 | |||
| 3e4ddf25d0 | |||
| 1a3b585209 | |||
| a5504231b5 | |||
| c63d042c57 | |||
| 70c8d43831 | |||
| 5837d3480c | |||
| ab1382b652 | |||
| 9553e46ed7 | |||
| 34b673844f | |||
| a2a840df93 | |||
| 5b23b67092 | |||
| f9d186d33a | |||
| 93f959b03b | |||
| 8f3f90a986 | |||
| c7a66ddf74 | |||
| b60d7e1476 | |||
| 4a1e099974 | |||
| 7280ff8f3d | |||
| f45213a276 | |||
| 346194bd8e | |||
| 959304bc0d | |||
| fffdd5170a | |||
| ec22828169 | |||
| ddc0c61692 | |||
| 1fbcb44ae6 | |||
| 7eaaf57d2b | |||
| fc643f2407 | |||
| 939b0a4297 | |||
| 234c8c9ef3 | |||
| 75bad643bd | |||
| 229e3ec184 | |||
| 774a6f1093 | |||
| 814728a6aa | |||
| 7654698a0e | |||
| ff785e5f17 | |||
| cc645de37b | |||
| cc62ee229e | |||
| 24476090df | |||
| 2aee2d312f | |||
| ee838422f7 | |||
| ea93fb7ac0 | |||
| 7bcb2a4081 | |||
| 2bf68e72d5 | |||
| 75579fcabd | |||
| 686e8d32e2 | |||
| 8d933cbfc4 | |||
| 5fef59beb4 | |||
| d467a068c2 | |||
| e055ffbdc7 | |||
| 53f00ae429 | |||
| 78a958ab61 | |||
| 4c51a523c8 | |||
| aadac369dc | |||
| 62dcb1aed0 | |||
| 47b0797fe1 | |||
| ed401cc29b | |||
| 34ea3fd17e | |||
| cbeceb0bea | |||
| cfc41a7cf2 | |||
| d7e798c15c | |||
| e382d40709 | |||
| ef081526fc | |||
| b06c000478 | |||
| eaa24dc7c8 | |||
| 34c669222d | |||
| 2fa6d70336 | |||
| 1b693c3845 | |||
| e6953000e8 | |||
| 207d6ae60d | |||
| 794928fadc | |||
| 288280aa8a | |||
| 1902bc0bfb | |||
| 68b400fbf3 | |||
| 9fff8e7392 | |||
| d8a5882890 | |||
| 5bf8aa2b49 | |||
| c08213a3fb | |||
| 73c6312391 | |||
| 928ca7bfb0 | |||
| ef7364b80e | |||
| 3d80b1371a | |||
| 624dd3e10c | |||
| 1e905eb4d5 | |||
| 781a6a9083 | |||
| 36f439b11c | |||
| 12bed8dc0d | |||
| a57ab07261 | |||
| afe25eec80 | |||
| c3a39d0ef3 | |||
| f621210eaa | |||
| fa6e5c5bff | |||
| df1c890a10 | |||
| 959b12f1b8 | |||
| d64d3c3a30 | |||
| 053c447a59 | |||
| c59b3c8b9c | |||
| ff00cdd728 | |||
| 5a3997789c | |||
| 1a57979f41 | |||
| edb9ed2311 | |||
| 30df3ea23a | |||
| 8288a0a2e3 | |||
| b9b4d390a6 | |||
| 1543b36a4b | |||
| e344fae6a1 | |||
| e9f9fd3727 | |||
| e2c3ee629d | |||
| 2e053f990c | |||
| a2dcd220af | |||
| 9d2ec718df | |||
| 2bfcca23e2 | |||
| 0952370cf5 | |||
| 652a31b714 | |||
| 7f635a2f1d | |||
| 5dc6a67bdc | |||
| 286bd362c4 | |||
| 035eb28e18 | |||
| c3f0c1e2e0 | |||
| 1395c27906 | |||
| 9f108b5d54 | |||
| d7504b1f52 | |||
| 6df0ae5d35 | |||
| 92e983a489 | |||
| e9531529ad | |||
| 956625dbc5 | |||
| 2f342af22f | |||
| 5c9bfe8c02 | |||
| d086ce43e0 | |||
| b85e5e6df4 | |||
| d12a358435 | |||
| ede8216751 | |||
| 9d1a0f3627 | |||
| 7279f5b6d5 | |||
| 392de819f8 | |||
| 55041eaab6 | |||
| 27bbaf633b | |||
| b3a9e1333d | |||
| 2af7913d95 | |||
| d593d4b3ef | |||
| a4f544ca14 | |||
| 6909d65613 | |||
| e967b75666 | |||
| 1527ba7381 | |||
| 428ec5b2a3 | |||
| 55c42ad681 | |||
| 96fc74c58f | |||
| 20f4e7e8a0 | |||
| ae40bcd58c | |||
| 554a1d8336 | |||
| bc7bd7a8b3 | |||
| 5c8292aaf4 | |||
| c613668fef | |||
| 1d763810ba | |||
| c574295012 | |||
| 3a44259b32 | |||
| 2885527b39 | |||
| 93ed433de3 | |||
| cf90bee8af | |||
| 029478f160 | |||
| 7a1aa6b563 | |||
| c82e180487 | |||
| 2b53cce79f | |||
| c3072fd5d2 | |||
| 3cec305524 | |||
| 0ee188e7df | |||
| cbd4b62a9b | |||
| 523a6670f4 | |||
| 8343eee7c0 | |||
| 8e79e00f95 | |||
| d7016055fb | |||
| feba835d18 | |||
| 077bfbde03 | |||
| 486ea76b98 | |||
| 4f66ea42af | |||
| 9dec8a51f0 | |||
| 5c667a2681 | |||
| 9ae84f5d6b | |||
| e51e922924 | |||
| 857c32bc21 | |||
| d5422534ae | |||
| 9fcc523485 | |||
| 67d1ab9106 | |||
| da6d2009e0 | |||
| 155132d336 | |||
| 08ddfe03d2 | |||
| 0eb2b9e756 | |||
| 5d4716a8a3 | |||
| 6161049a31 | |||
| fdfe9d836e | |||
| a9282edf79 | |||
| aa8f669a3d | |||
| 5c2b253f29 | |||
| 32490710dc | |||
| 6a1e0c3127 | |||
| ad1863dd3b | |||
| 60f9834ac6 | |||
| 2bc346ec5b | |||
| 5ee3358a92 | |||
| d5e507fc7f | |||
| 6954783d9d | |||
| 7c11cb9b68 | |||
| 2dddec5e14 | |||
| 620491a649 | |||
| 7edfc57228 | |||
| bd3cf73e6e | |||
| 177505b757 | |||
| 76a37461bf | |||
| 23d41b927a | |||
| 795c159e7e | |||
| bb658fe29b | |||
| 095c9a6974 | |||
| 9d9d8cd59f | |||
| 1657af1567 | |||
| acb93d1aed | |||
| 889ad3d4e6 | |||
| 220741ff8b | |||
| 4e3db462ed | |||
| 073afb31ec | |||
| 455167db9a | |||
| 429c7a03cb | |||
| dca7299b9a | |||
| 8229adc495 | |||
| 92c5a38090 | |||
| a465b67c9b | |||
| 01b17a2dcc | |||
| 0b61c3f233 | |||
| 88bc821d79 | |||
| 56c98f7897 | |||
| c3f7aac4f9 | |||
| 47247be77c | |||
| 449ac4ca2a | |||
| d2738ca25e | |||
| 6db57b8a4d | |||
| 7567a0bb13 | |||
| 9def21ac30 | |||
| 8fd390e354 | |||
| c3b3df9f22 | |||
| afefedc0b0 | |||
| 4a5b66de9a | |||
| 842e1b6358 | |||
| 82d48b4e6d | |||
| cf3884c9ad | |||
| fa994baada | |||
| 64dde206b6 | |||
| f4b3554d9e | |||
| d9ea7e6211 | |||
| ac49260792 | |||
| 5de46b2a1c | |||
| 9c1f4777d1 | |||
| 1fdf9f4555 | |||
| 690d470c71 | |||
| b0d90e3688 | |||
| 9c18e7c990 | |||
| 731041cb6a | |||
| 3a1433fb98 | |||
| b0087b634d | |||
| e1921929ad | |||
| 146690547d | |||
| 05d61eca0c | |||
| 1d2d45f331 | |||
| b50305e7fe | |||
| ee3676b834 | |||
| a97ae66a1d | |||
| 93538def65 | |||
| e5067b6611 | |||
| 0629fb62d7 | |||
| 0177cf3ea4 | |||
| 658aca1469 | |||
| 03df4c7759 | |||
| 74b1233917 | |||
| 0d4f8f4e95 | |||
| ddd3f2084d | |||
| dba3ec9428 | |||
| 9de361a1b9 | |||
| c772fb7976 | |||
| 45e0ac040a | |||
| 730770304e | |||
| 2fc3c19a24 | |||
| a12cb07327 | |||
| fecb7f794c | |||
| 7f5d484210 | |||
| 201ffe4487 | |||
| c0c959b1be | |||
| 29d2a0f198 | |||
| 3d6258365f | |||
| 5a08f49995 | |||
| e30bf95989 | |||
| 31584607a5 | |||
| cf3b9cc859 | |||
| 7f803a88e8 | |||
| bc181bc3ff | |||
| 15e60e9e98 | |||
| c557efba3b | |||
| 62a61bf986 | |||
| 95ffcb67d3 | |||
| 7b61444538 | |||
| 26ebc9680e | |||
| a37066776a | |||
| d5e2b6bc3c | |||
| 8dfbe2bf56 | |||
| b7b0f96cc7 | |||
| 3f4b46ac9e | |||
| 80e413e922 | |||
| 8047e43d46 | |||
| 017a5f75a3 | |||
| 61fd2bb070 | |||
| b16cc5d197 | |||
| e6f4a83da6 | |||
| 1a8bae5b2f | |||
| e8eb285a59 | |||
| b508d28123 | |||
| 62b551798f | |||
| dfbebe395c | |||
| 85280b5bf4 | |||
| fb53cfd9b0 | |||
| 92d2123d8d | |||
| ec3de28ae5 | |||
| 86dc136fa9 | |||
| 172f316ac2 | |||
| eb47d4fd95 | |||
| ffe4085dbb | |||
| 2682f77ba6 | |||
| a81f87b9f1 | |||
| 57133e6ae6 | |||
| 2dbc0dcb50 | |||
| 300b0d3e6a | |||
| a3b4b6c23c | |||
| d0c71195dd | |||
| c939e5e302 | |||
| 61b895d9ae | |||
| bd38b9c44b | |||
| 019f52acee | |||
| b2d1cc4090 | |||
| 59c25ebe03 | |||
| 8dff00a7b8 | |||
| 11d441a580 | |||
| f9013a5e66 | |||
| 941d9da08c | |||
| 9d7f86a66f | |||
| f5030b49e5 | |||
| f5dfc407b0 | |||
| 708bfa9e80 | |||
| ef68938099 | |||
| bfee4bcb1e | |||
| 116e5a5e79 | |||
| 290b4b61c9 | |||
| e3677059ae | |||
| 1c45cb936a | |||
| bf2487eda8 | |||
| 81ab73f686 | |||
| 2dc9f42a6e | |||
| bb2ec3f3ab | |||
| e731f73946 | |||
| f4bbdc7d30 | |||
| a8aada667d | |||
| 5554a4c9f0 | |||
| e305fd8705 | |||
| 85c277e601 | |||
| d3b0c4bfe5 | |||
| 275dc50223 | |||
| d5867f075a | |||
| 9442285526 | |||
| ca5b52a012 | |||
| d2e7eb1e03 | |||
| b483065c17 | |||
| ff757e2d63 | |||
| 6d83f928b0 | |||
| 3d2ed21b34 | |||
| 8e4e40557a | |||
| be88627759 | |||
| 6774055233 | |||
| 519c10573d | |||
| 564f695ea5 | |||
| da0634c124 | |||
| 59d9cf27eb | |||
| 1f9a434881 | |||
| 4100098440 | |||
| 04cc804566 | |||
| 75bc8bd379 | |||
| 26e9ba84a8 | |||
| fcdf3decda | |||
| 10fa02c5d2 | |||
| a0b433c8e1 | |||
| a7d53e2326 | |||
| 971bba57b3 | |||
| f1c1a513ca | |||
| 0e63742d90 | |||
| 177251677d | |||
| 1a87d22109 | |||
| 57fb442eff | |||
| caa40b8dd3 | |||
| 2758353380 | |||
| fe1a956715 | |||
| c05312f151 | |||
| 3a22385a9f | |||
| ec1d91b2af | |||
| 3e467b1b21 | |||
| 1f50f37da3 | |||
| 5966316771 | |||
| 130ee246e2 | |||
| 039ae46581 | |||
| c99997d6f1 | |||
| 85fc3b934b | |||
| eaf4b5920f | |||
| 77b1ba84d6 | |||
| 2d7deebf2a | |||
| 127d0309f6 | |||
| f309b9d881 | |||
| f432255759 | |||
| 641522a750 | |||
| d49cb6613e | |||
| 90af7c73ef | |||
| f64c36366c | |||
| 3251681207 | |||
| c9346cdb67 | |||
| 1c6e1bdb72 | |||
| 26f41a5042 | |||
| 718f8b78ec | |||
| 7eeeda5acd | |||
| fcb0c77c7a | |||
| 25bcfbf59e | |||
| 50c153926e | |||
| d8739fc659 | |||
| 8ef2d4413f | |||
| 2a8cd1c37c | |||
| db83396210 | |||
| d332c41e71 | |||
| 3a18496f5a | |||
| 81484e7801 | |||
| 6335106de9 | |||
| c9da89254b | |||
| eb2d869f71 | |||
| f1e92fe2a3 | |||
| b5400c54df | |||
| a4de6016f8 | |||
| 4807909e3f | |||
| dd0884b707 | |||
| e1634ca6cb | |||
| 7b6339cdd2 | |||
| 794af9b7f7 | |||
| 651a6edc5c | |||
| ada5edce88 | |||
| 41ce4ca9fc | |||
| fe768d729d | |||
| 1850c54b8c | |||
| bc9c69f45e | |||
| 937ae9b578 | |||
| 4eb451165f | |||
| 3df4750a37 | |||
| 27d32ac5d9 | |||
| 2a067fbe93 | |||
| b5f299cc17 | |||
| 0673d5f44f | |||
| 56f18e35d4 | |||
| b5cb5f17fb | |||
| 1bd82bf4c6 | |||
| a8c49dfa7d | |||
| b1fa9d2b06 | |||
| caad0b38ac | |||
| b9b4ae6ec2 | |||
| f7fe6cf1a6 | |||
| 15e2939f7a | |||
| fc23f65d4f | |||
| 550afb6ffb | |||
| b3e094ec1d | |||
| a736ae0b1a | |||
| 59e30f1a84 | |||
| 3bf1b9c6ca | |||
| 264102d245 | |||
| 8c94450c42 | |||
| d956c2e0ba | |||
| 7cf666f0fc | |||
| e0a9f16a10 | |||
| b9c1d6f354 | |||
| 0495d406d6 | |||
| 24f950dd5a | |||
| 439bc9bddc | |||
| c5009c2601 | |||
| f9cc6ffcbf | |||
| 35c2754c88 | |||
| f4ca8de737 | |||
| 254acd8ff2 | |||
| 75bc88d943 | |||
| ee35599f93 | |||
| 384668bbc8 | |||
| 5c53c1e599 | |||
| bc9c0c1598 | |||
| c40f6b6ead | |||
| d42a3fb3c7 | |||
| 930102fe6e | |||
| d60fe8d4ef | |||
| 3a9cd38784 | |||
| 476f78c300 | |||
| 852b79283e | |||
| d006752946 | |||
| 55a917cd69 | |||
| 07d1e453a4 | |||
| 3d6f2e3b68 | |||
| b7932df9cc | |||
| 540c9474fe | |||
| b31fe73fb8 | |||
| 83cbb7f8de | |||
| 9aba96a86e | |||
| 768bebff23 | |||
| d874a07541 | |||
| babb37c1df | |||
| 924c96ce92 | |||
| 33d1139101 | |||
| 4bc8b46d65 | |||
| e7f2a2aa0e | |||
| 1b0f782c56 | |||
| 1fc2038979 | |||
| a8f82911e3 | |||
| 6e21043842 | |||
| f3bea731ce | |||
| 2b5372655c | |||
| fa78ede747 | |||
| 16b312867c | |||
| 17fb6c489e | |||
| d2bb9d024e | |||
| 0c6938faa9 | |||
| 4dc3aeaeec | |||
| bd8b926dd8 | |||
| d35e7d4cf6 | |||
| 45cd8b8abd | |||
| a19f80424e | |||
| 9123d31176 | |||
| e968294aba | |||
| 29b476f32b | |||
| 50df65bda7 | |||
| 623e720e02 | |||
| 20562f06b6 | |||
| 46ab6c1a30 | |||
| ba62d19b6d | |||
| 571c1d3b8c | |||
| b240768ce0 | |||
| eefc497b9e | |||
| 6b1dfecf71 | |||
| 0bc9dd6bec | |||
| 29e6fdfac4 | |||
| f8b4ada69f | |||
| e9e849a017 | |||
| 6fb91b253f | |||
| cf93abaf4e | |||
| c7ed230961 | |||
| b796e87d6b | |||
| 209484e1b7 | |||
| daa5285092 | |||
| 384e3cca77 | |||
| 4b7ee4492c | |||
| 56a6054dc4 | |||
| 1102e0618e | |||
| 910ac3563e | |||
| ff1384d12d | |||
| f2e7d7ca99 | |||
| ef4aa8e27a | |||
| c3945c6c5a | |||
| 3e849586a0 | |||
| 33cc71dc55 | |||
| 29a18c93ca | |||
| 70fcaa1c49 | |||
| d3b32821c0 | |||
| ddb3f2dd12 | |||
| a7db84a442 | |||
| 9772dc39d7 | |||
| 7710d53bca | |||
| 8cf718fd2b | |||
| 2796f880f3 | |||
| e1ead323df | |||
| c9239436b8 | |||
| 2e108d5dea | |||
| 67f8eb429c | |||
| 49fe800281 | |||
| e014a57910 | |||
| ebeeadfc43 | |||
| 7817b4ec2a | |||
| c347df19f3 | |||
| e2df14b974 | |||
| 4795c660a0 | |||
| 2df4b9b4ca | |||
| 2b4358bec8 | |||
| 9022aa8a9c | |||
| 5aa0ab69b2 | |||
| 5c126afd71 | |||
| b954190cf3 | |||
| 3ef5ad2e1c | |||
| 29d0b3a537 | |||
| 1c69008a7a | |||
| fa0451afe0 | |||
| bccf898276 | |||
| 950c3f2b1a | |||
| 186df5a08a | |||
| 1dc8e0ba33 | |||
| c8dd97526c | |||
| 42828944c7 | |||
| a72fcdd4fe | |||
| f98a859dde | |||
| 23b1528cce | |||
| a6870e4325 | |||
| efc7773335 | |||
| 6f5dddd9ae | |||
| fd67e04dca | |||
| 53cf004739 | |||
| 28e69de773 | |||
| 4b131dfb02 | |||
| c8e01c19f2 | |||
| a011e6f5c7 | |||
| a624111c73 | |||
| 8caabd07dd | |||
| 921cef77db | |||
| fc81f4be42 | |||
| 340f5f231e | |||
| aa732fc5ed | |||
| 396e0ee140 | |||
| 992798504c | |||
| 442b8afa61 | |||
| 521c3abb5a | |||
| 283c31006a | |||
| 64f6355abb | |||
| 5f832a2823 | |||
| 64a913454f | |||
| aae649e010 | |||
| e2c386bbda | |||
| 51dda0ac12 | |||
| c0c43aee9c | |||
| 91690a50f8 | |||
| a046a6b927 | |||
| 77bd3a4134 | |||
| 45a3be0a76 | |||
| 54b8e815af | |||
| 1dd01bd395 | |||
| f5e8fc9bd3 | |||
| ab1304fe5f | |||
| f3854a8731 | |||
| 44c73fc93e | |||
| 7d78597b9a | |||
| 57e9ecb209 | |||
| 3db013b626 | |||
| afc7563b24 | |||
| 22cf833a34 | |||
| 2e775b5262 | |||
| 05364d4c23 | |||
| 350d68054c | |||
| 08e1562de0 | |||
| c5f210936b | |||
| 9db2844e87 | |||
| a495b98694 | |||
| 4ac604b136 | |||
| 3c62501f0a | |||
| 083ddf40a3 | |||
| dde7cc75a0 | |||
| 0d0596d6b6 | |||
| a6abd3f1a7 | |||
| cbd25963f9 | |||
| cda2a6f415 | |||
| a0adac936c | |||
| 67b4233ea1 | |||
| 69cb0acc50 | |||
| 9d56e1bb61 | |||
| 6d90a23057 | |||
| 2268cae9c7 | |||
| 3bd605b1f7 | |||
| 7e809a5db0 | |||
| 0e699744a3 | |||
| dd1de36f6d | |||
| 8160e9283f | |||
| 746a163c9d | |||
| ff84e38d23 | |||
| 5781c6106a | |||
| 17438ab7ee | |||
| 50dda86053 | |||
| e5966647cd | |||
| bb39e71734 | |||
| 0bf358c919 | |||
| bfa4a88eda | |||
| 0b614eeb25 | |||
| ce8ba6dc82 | |||
| 8ed29abe7b | |||
| 6b81436aae | |||
| ccb0fab35d | |||
| 4f9dc7c67e | |||
| cb5a5072b5 | |||
| 348af09737 | |||
| b82b2a69a8 | |||
| 8cae227d6b | |||
| d987c847d3 | |||
| 7cff6e5437 | |||
| 1e156b6ba7 | |||
| 4ca1486f75 | |||
| 3a3b4b96b0 | |||
| b35787f7cd | |||
| 6bd91b1c77 | |||
| d4635b2267 | |||
| 3ad4f6e3f3 | |||
| d93a56dc36 | |||
| 8fa790e0ed | |||
| 5b06edc9b9 | |||
| abecc96179 | |||
| 6600ec7b23 | |||
| 328c4d930c | |||
| 9844d1bb9c | |||
| ded9e7358e | |||
| ae376363cb | |||
| 527ae5e0b7 | |||
| 61d895508c | |||
| 56267edeaf | |||
| 07850eefce | |||
| a207bb6bd0 | |||
| f5a7c8f633 | |||
| 644dd5bb0a | |||
| 269e4cda9a | |||
| 38c5ba1349 | |||
| 9200b1977e | |||
| c33a8d2d5b | |||
| c1466a5ad8 | |||
| 8c4b009935 | |||
| 79533164f7 | |||
| c68b50d4cf | |||
| b26875768e | |||
| b8cbe0700e | |||
| 99f88f8833 | |||
| 0d1d7624e7 | |||
| 4b14bc3b67 | |||
| 7f9e557b03 | |||
| 13785dbbb8 | |||
| e09f019412 | |||
| 49bc4dee56 | |||
| 9935b3b514 | |||
| a7892bd150 | |||
| b50dc9cd04 | |||
| b459ddd7cf | |||
| 872b67b636 | |||
| 5ba09ec2d4 | |||
| 6ea8ccc928 | |||
| 6ac7bc692c | |||
| 60297a5c6d | |||
| 81c58afbde | |||
| 7d419f104c | |||
| 38bce0a9ac | |||
| 62f6480088 | |||
| 2d3b5ca941 | |||
| 344ec4ef81 | |||
| fa04fa1e3d | |||
| fe110253ae | |||
| fcb88a40e8 | |||
| 6a3f930eee | |||
| fabbffc0e6 | |||
| 8da18af6ce | |||
| b751fa68a1 | |||
| 040287fadd | |||
| 4d9476ab25 | |||
| 5124e75a18 | |||
| 60b5374440 | |||
| 3aa56a3a8f | |||
| 6b34ca6121 | |||
| c190261131 | |||
| 2a556dfab5 | |||
| 465d659076 | |||
| 8269bf170d | |||
| f387667258 | |||
| 8587f5c5cc | |||
| 6ec799058d | |||
| 8d9c451dfc | |||
| 6aab01dbda | |||
| 65e87052e7 | |||
| 1694870557 | |||
| 0b184896e6 | |||
| b8dbe0ee19 | |||
| b5744ef1a3 | |||
| 046ad6b37d | |||
| 2bf674bebd | |||
| 56bc19d278 | |||
| 96f7d4141d | |||
| 870fce8d50 | |||
| 824a6d73f1 | |||
| 1dc0c18121 | |||
| 3f3972e0b1 | |||
| 8f1767c5e8 | |||
| 26d7a431d5 | |||
| 18682525c2 | |||
| 98849c317f | |||
| a2e40430e8 | |||
| 859a9f5698 | |||
| 67b7b06817 | |||
| 84b55356a5 | |||
| 66a5927b7c | |||
| fa0e47d77d | |||
| 0bef0d525f | |||
| 86f522d0c9 | |||
| 10022f6414 | |||
| 1ac061beb7 | |||
| ff32736d78 | |||
| 95006b300d | |||
| 302bf7d504 | |||
| 038460ed97 | |||
| 845a6c2876 | |||
| ae0b8ba34c | |||
| 415c1d5912 | |||
| 71745546be | |||
| 6f68005537 | |||
| b146a73b27 | |||
| e17be1fa25 | |||
| 38cf12aa5e | |||
| 1de6249a3c | |||
| f0b4170aa3 | |||
| 1d7b661ae1 | |||
| e23732735a | |||
| 5d8e324fb4 | |||
| ea15bbce36 | |||
| 660e98a404 | |||
| 6ea08e236b | |||
| d075042a12 | |||
| 400a37efa9 | |||
| 8ca54d06f9 | |||
| b1bb1b3f9f | |||
| 457e037cdc | |||
| 73f27d8a3a | |||
| 38682cebf4 | |||
| fb2ad84ed2 | |||
| 5af064db83 | |||
| ac0489e1a3 | |||
| ae210a72e8 | |||
| 6fe32d305b | |||
| 6fd928a1ba | |||
| 39c93a46de | |||
| a14c0f795c | |||
| 7a41d6da21 | |||
| b70d9a95fa | |||
| 675b472160 | |||
| e541251cc0 | |||
| 21f2b83471 | |||
| 6cf948cd0f | |||
| 68faf508d0 | |||
| 554e137315 | |||
| 2deee567c7 | |||
| 163b2acd07 | |||
| 2152f9d015 | |||
| fbe183c3be | |||
| 02d5426956 | |||
| e2a5f77983 | |||
| aa84a62631 | |||
| ce1151874d | |||
| ae6f303ece | |||
| 659e695afd | |||
| 02618853d0 | |||
| df41d749fa | |||
| 46899d89bc | |||
| 228e986680 | |||
| 6217711e57 | |||
| 7a75c908a4 | |||
| d50dbd0944 | |||
| d9b12c2f80 | |||
| e1012ccb6c | |||
| ae53260c58 | |||
| d611752109 | |||
| edb816bdb6 | |||
| 512c336eb7 | |||
| adb6b347c4 | |||
| 8a48ce6e48 | |||
| f246b87326 | |||
| d41bfe5da7 | |||
| 9188986f5c | |||
| 85c8d48d39 | |||
| fb0059fed4 | |||
| b416e79b8a | |||
| 962e1df4a7 | |||
| 07f582c35c | |||
| 9327cf5c05 | |||
| c90103da14 | |||
| d805cb2dfa | |||
| d92d4a53ed | |||
| 030e8feb12 | |||
| 730a70272d | |||
| cedd0588c2 | |||
| a52932599d | |||
| 0c3abcf6c7 | |||
| eeaf48d01b | |||
| 0f502afae5 | |||
| 87c09ecca2 | |||
| 2aae827e66 | |||
| 808f6945ed | |||
| 01c33d20f5 | |||
| 0ee3d1caa6 | |||
| 3d28d2261e | |||
| 36bf39fe82 | |||
| 4ce3de09e8 | |||
| fab111163c | |||
| cf4c8c8cd5 | |||
| 6b43bf216a | |||
| 12ec52c154 | |||
| 1864e3933b | |||
| 88d0c0f220 | |||
| 79bfba27f5 | |||
| 77d43b339c | |||
| 5620490800 | |||
| e851f93806 | |||
| 227a21b10c | |||
| 4f94e31468 | |||
| e0262d6411 | |||
| c6c41229ab | |||
| ef7db84d52 | |||
| f9e252c022 | |||
| 550287fe8a | |||
| 6f0b29e658 | |||
| 15fd911d46 | |||
| 1877ef828a | |||
| 8dd3c36c1e | |||
| 5f03403335 | |||
| ecff9feb1a | |||
| 96b11d241b | |||
| 9b9deb3f0c | |||
| a78c712121 | |||
| e70eed28c3 | |||
| ca3968c373 | |||
| 88b54ce379 | |||
| 11dfa23c78 | |||
| 5b40e3c758 | |||
| c1f2bc3e9a | |||
| 1f39c55d0b | |||
| fcbd9c12dc | |||
| 7474e5f7a3 | |||
| 3686142e29 | |||
| 7a47a0a5c5 | |||
| 7c473e176f | |||
| 2d96c0e03e | |||
| 9b60b2e36b | |||
| 6299f310c7 | |||
| e5342e8f86 | |||
| 914ce58eff | |||
| 64047da4c9 | |||
| c1ca8b73a0 | |||
| 164c2e902f | |||
| 5474bf3e74 | |||
| 45326da58f | |||
| f409840211 | |||
| e098ae1382 | |||
| 6cbdbf4583 | |||
| a3289c4f51 | |||
| 88b0078383 | |||
| f028399417 | |||
| 47a9334ec1 | |||
| b9cb4174a5 | |||
| a2e455f7a2 | |||
| 406afa3877 | |||
| 756083d08a | |||
| 65f0ea7870 | |||
| e192a14372 | |||
| 1fcc8aa31a | |||
| c6348928d2 | |||
| 4de4a301c5 | |||
| 1aab740694 | |||
| 526def20a0 | |||
| cc0fd59190 | |||
| f0203f5781 | |||
| 8bc0ebdba5 | |||
| 68ab6871eb | |||
| 60a232cfbf | |||
| 35e31bd331 | |||
| 956b6643ea | |||
| a62907419b | |||
| ffba0dfbfa | |||
| d9a46534aa | |||
| f361ec16b6 | |||
| 87a5096293 | |||
| 8e1e9943a4 | |||
| 6bbcc99187 | |||
| 8939a5744a | |||
| f49e22cff2 | |||
| fe763ec715 | |||
| 7bd8b98124 | |||
| 8ed97e6961 | |||
| ba02f818b5 | |||
| 526df4b3c0 | |||
| f78dbc1da5 | |||
| 6bd1bbb04b | |||
| 781d35d251 | |||
| db57736639 | |||
| cf5a8be51e | |||
| 87ecea3154 | |||
| 295b3c8da3 | |||
| ecaa6d74f8 | |||
| 7a6a38ee78 | |||
| f964984b1c | |||
| 027815798c | |||
| f388816f9a | |||
| a9569c6fb1 | |||
| c7132c0c1f | |||
| c34c2e2de1 | |||
| 0d534e64f5 | |||
| 84a5bcb17a | |||
| 3dbc05d8ef | |||
| f32d123ed2 | |||
| b7280dbd42 | |||
| 96b9a35756 | |||
| 60194d73fc | |||
| d13d33dd78 | |||
| 1147218129 | |||
| fe8e68c6ed | |||
| 2289863e20 | |||
| de87f835bb | |||
| a8aef4e9ee | |||
| 6b878d7309 | |||
| f01c2932c7 | |||
| 2b404d9a39 | |||
| d1e48591d7 | |||
| 358203e655 | |||
| 8a43201996 | |||
| f179fe6d8a | |||
| 43fead371b | |||
| de2295f507 | |||
| 7afb67b23e | |||
| 4dadc516d9 | |||
| c846839ca8 | |||
| 0a79fd2282 | |||
| 9cc08cb84e | |||
| 4fbc1b4674 | |||
| cd1f98db96 | |||
| 1f5bf96273 | |||
| 05a6d882c4 | |||
| 59f7533363 | |||
| a8efaa7916 | |||
| d762812dfb | |||
| 97d8b4384a | |||
| 5836acc0d9 | |||
| f03b1e130e | |||
| 00518c4a3a | |||
| 0d50c1184a | |||
| 67ea3368f6 | |||
| 4c32b140aa | |||
| e53cc4d17d | |||
| 6a20546b1d | |||
| 7f21dc83b7 | |||
| 930061a990 | |||
| 96adce80d6 | |||
| 44ffeabbf1 | |||
| 6a5f07504d | |||
| 40698ef12d | |||
| 949aa3e079 | |||
| a90bdd558c | |||
| f85e8e6e43 | |||
| 105bbcefe4 | |||
| 11b10c5041 | |||
| 7a44eac02c | |||
| c22e6aa43b | |||
| 05e2c97331 | |||
| 5e4f8e627d | |||
| cda05dcaf7 | |||
| 1bde0d549e | |||
| 9766daa1b2 | |||
| ae19f5ba96 | |||
| 33549f847a | |||
| 5a0958d2f6 | |||
| 22b4a62161 | |||
| 4723cff696 | |||
| dfb4c913a8 | |||
| 9e099237d3 | |||
| c057503a1c | |||
| 1f71792b84 | |||
| 53e0380480 | |||
| b75bd3a130 | |||
| 46e016be71 | |||
| d044ed3955 | |||
| 007afa9988 | |||
| 3802564e09 | |||
| d0bc38e0a5 | |||
| d23559f3c9 | |||
| d60f220e18 | |||
| b3d740a7f7 | |||
| ed9de62be4 | |||
| 8fe10f0346 | |||
| 311b063378 | |||
| 455f38f09e | |||
| c17cb214b3 | |||
| 4c0e1f8907 | |||
| 2ba221f9f3 | |||
| c61781881b | |||
| 9856a971b0 | |||
| 3989471e6d | |||
| 76f67c409a | |||
| 8fc1bff114 | |||
| f027156aa8 | |||
| 2a93b82078 | |||
| ddf8f4b7d3 | |||
| 173906da35 | |||
| f54b875cbe | |||
| 4bce6207cd | |||
| fc989401db | |||
| d921604444 | |||
| d70c7b57dd | |||
| f433f8d136 | |||
| d9e051ba70 | |||
| c2070e9977 | |||
| 916afcc290 | |||
| 24749b3393 | |||
| 365a18b272 | |||
| 767d383f2c | |||
| 175f6818bd | |||
| 9da8beac56 | |||
| 1a03adebb4 | |||
| 4397f709be | |||
| 2d29aeaf12 | |||
| 1ae6aa1bef | |||
| e20f0746e6 | |||
| db9c3e69d6 | |||
| 0591633876 | |||
| 17119a7324 | |||
| aa0b85bb80 | |||
| f23bc545d7 | |||
| 9ca97814d3 | |||
| bc15365ca5 | |||
| ebe47b0f95 | |||
| 87163d971a | |||
| 8eff95d047 | |||
| 2784d42ea8 | |||
| 7109cbbdd2 | |||
| 068130bafc | |||
| 4a906fbff2 | |||
| ed4857dfcf | |||
| bbf3970981 | |||
| f777086b59 | |||
| 426a2b1967 | |||
| bc85cf8fb8 | |||
| 7483217fe0 | |||
| 50bd93709f | |||
| ba17882426 | |||
| a3f872f29b | |||
| fc16a68f48 | |||
| 477587f566 | |||
| cfd966d7c4 | |||
| 5cd0e5f10c | |||
| 3c652197bc | |||
| f08a695702 | |||
| 54bb1cf358 | |||
| 014b440244 | |||
| 79fd0a8b78 | |||
| 4b1d3d79e2 | |||
| a3acd3e0fe | |||
| 0d4350ca62 | |||
| ade0d0e1f2 | |||
| d2e18be452 | |||
| c7d7de315b | |||
| a8f107756e | |||
| 053065ba23 | 
							
								
								
									
										51
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
build/
 | 
			
		||||
dist/
 | 
			
		||||
torch.egg-info/
 | 
			
		||||
*/**/__pycache__
 | 
			
		||||
torch/version.py
 | 
			
		||||
torch/csrc/generic/TensorMethods.cpp
 | 
			
		||||
torch/lib/*.so*
 | 
			
		||||
torch/lib/*.a*
 | 
			
		||||
torch/lib/*.dylib*
 | 
			
		||||
torch/lib/*.h
 | 
			
		||||
torch/lib/build
 | 
			
		||||
torch/lib/tmp_install
 | 
			
		||||
torch/lib/include
 | 
			
		||||
torch/lib/torch_shm_manager
 | 
			
		||||
torch/csrc/cudnn/cuDNN.cpp
 | 
			
		||||
torch/csrc/nn/THNN.cwrap
 | 
			
		||||
torch/csrc/nn/THNN.cpp
 | 
			
		||||
torch/csrc/nn/THCUNN.cwrap
 | 
			
		||||
torch/csrc/nn/THCUNN.cpp
 | 
			
		||||
torch/csrc/nn/THNN_generic.cwrap
 | 
			
		||||
torch/csrc/nn/THNN_generic.cpp
 | 
			
		||||
torch/csrc/nn/THNN_generic.h
 | 
			
		||||
torch/csrc/generated
 | 
			
		||||
docs/src/**/*
 | 
			
		||||
test/data/legacy_modules.t7
 | 
			
		||||
test/data/gpu_tensors.pt
 | 
			
		||||
test/htmlcov
 | 
			
		||||
test/.coverage
 | 
			
		||||
*/*.pyc
 | 
			
		||||
*/**/*.pyc
 | 
			
		||||
*/**/**/*.pyc
 | 
			
		||||
*/**/**/**/*.pyc
 | 
			
		||||
*/**/**/**/**/*.pyc
 | 
			
		||||
*/*.so*
 | 
			
		||||
*/**/*.so*
 | 
			
		||||
*/**/*.dylib*
 | 
			
		||||
test/data/legacy_serialized.pt
 | 
			
		||||
test/data/linear.pt
 | 
			
		||||
 | 
			
		||||
# IPython notebook checkpoints
 | 
			
		||||
.ipynb_checkpoints
 | 
			
		||||
 | 
			
		||||
# Editor temporaries
 | 
			
		||||
*.swn
 | 
			
		||||
*.swo
 | 
			
		||||
*.swp
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
# OSX dir files
 | 
			
		||||
.DS_Store
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
# https://travis-ci.org/pytorch/pytorch
 | 
			
		||||
language: python
 | 
			
		||||
dist: trusty
 | 
			
		||||
python:
 | 
			
		||||
    - 2.7.9
 | 
			
		||||
    - 2.7
 | 
			
		||||
    - 3.5
 | 
			
		||||
    - 3.6
 | 
			
		||||
    - nightly
 | 
			
		||||
 | 
			
		||||
cache:
 | 
			
		||||
    - ccache
 | 
			
		||||
    - directories:
 | 
			
		||||
        - $HOME/.ccache
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
    - unset CCACHE_DISABLE
 | 
			
		||||
    - export CCACHE_DIR=$HOME/.ccache
 | 
			
		||||
    - export CC="ccache gcc-4.8"
 | 
			
		||||
    - export CXX="ccache g++-4.8"
 | 
			
		||||
    - ccache --show-stats
 | 
			
		||||
    - travis_retry pip install --upgrade pip setuptools wheel
 | 
			
		||||
    - travis_retry pip install -r requirements.txt --only-binary=scipy
 | 
			
		||||
    - python setup.py install
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
    - OMP_NUM_THREADS=2 ./test/run_test.sh
 | 
			
		||||
 | 
			
		||||
addons:
 | 
			
		||||
    apt:
 | 
			
		||||
        sources:
 | 
			
		||||
            - ubuntu-toolchain-r-test
 | 
			
		||||
        packages:
 | 
			
		||||
            - gcc-4.8
 | 
			
		||||
            - g++-4.8
 | 
			
		||||
 | 
			
		||||
# This reportedly works around an issue downloading packages from pypi on
 | 
			
		||||
# travis.  Consider removing this after the underlying issue is fixed.
 | 
			
		||||
# https://github.com/travis-ci/travis-ci/issues/2389
 | 
			
		||||
sudo: false
 | 
			
		||||
 | 
			
		||||
matrix:
 | 
			
		||||
    fast_finish: true
 | 
			
		||||
    include:
 | 
			
		||||
        env: LINT_CHECK
 | 
			
		||||
        python: "2.7"
 | 
			
		||||
        addons: true
 | 
			
		||||
        install: pip install flake8
 | 
			
		||||
        script: flake8
 | 
			
		||||
							
								
								
									
										185
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,185 @@
 | 
			
		||||
## Contributing to PyTorch
 | 
			
		||||
 | 
			
		||||
If you are interested in contributing to PyTorch, your contributions will fall
 | 
			
		||||
into two categories:
 | 
			
		||||
1. You want to propose a new Feature and implement it
 | 
			
		||||
    - post about your intended feature, and we shall discuss the design and
 | 
			
		||||
    implementation. Once we agree that the plan looks good, go ahead and implement it.
 | 
			
		||||
2. You want to implement a feature or bug-fix for an outstanding issue
 | 
			
		||||
    - Look at the outstanding issues here: https://github.com/pytorch/pytorch/issues
 | 
			
		||||
    - Especially look at the Low Priority and Medium Priority issues
 | 
			
		||||
    - Pick an issue and comment on the task that you want to work on this feature
 | 
			
		||||
    - If you need more context on a particular issue, please ask and we shall provide.
 | 
			
		||||
 | 
			
		||||
Once you finish implementing a feature or bugfix, please send a Pull Request to
 | 
			
		||||
https://github.com/pytorch/pytorch
 | 
			
		||||
 | 
			
		||||
If you are not familiar with creating a Pull Request, here are some guides:
 | 
			
		||||
- http://stackoverflow.com/questions/14680711/how-to-do-a-github-pull-request
 | 
			
		||||
- https://help.github.com/articles/creating-a-pull-request/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Developing locally with PyTorch
 | 
			
		||||
 | 
			
		||||
To locally develop with PyTorch, here are some tips:
 | 
			
		||||
 | 
			
		||||
1. Uninstall all existing pytorch installs
 | 
			
		||||
```
 | 
			
		||||
conda uninstall pytorch
 | 
			
		||||
pip uninstall torch
 | 
			
		||||
pip uninstall torch # run this command twice
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
2. Locally clone a copy of PyTorch from source:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
git clone https://github.com/pytorch/pytorch
 | 
			
		||||
cd pytorch
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
3. Install PyTorch in `build develop` mode:
 | 
			
		||||
 | 
			
		||||
A full set of instructions on installing PyTorch from Source are here:
 | 
			
		||||
https://github.com/pytorch/pytorch#from-source
 | 
			
		||||
 | 
			
		||||
The change you have to make is to replace
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
python setup.py install
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
with
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
python setup.py build develop
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This is especially useful if you are only changing Python files.
 | 
			
		||||
 | 
			
		||||
This mode will symlink the python files from the current local source tree into the
 | 
			
		||||
python install.
 | 
			
		||||
 | 
			
		||||
Hence, if you modify a python file, you do not need to reinstall pytorch again and again.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
- Install local pytorch in `build develop` mode
 | 
			
		||||
- modify your python file `torch/__init__.py` (for example)
 | 
			
		||||
- test functionality
 | 
			
		||||
- modify your python file `torch/__init__.py`
 | 
			
		||||
- test functionality
 | 
			
		||||
- modify your python file `torch/__init__.py`
 | 
			
		||||
- test functionality
 | 
			
		||||
 | 
			
		||||
You do not need to repeatedly install after modifying python files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Writing documentation
 | 
			
		||||
 | 
			
		||||
PyTorch uses [Google style](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
 | 
			
		||||
for formatting docstrings. Length of line inside docstrings block must be limited to 80 characters to
 | 
			
		||||
fit into Jupyter documentation popups.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Managing multiple build trees
 | 
			
		||||
 | 
			
		||||
One downside to using `python setup.py develop` is that your development
 | 
			
		||||
version of pytorch will be installed globally on your account (e.g., if
 | 
			
		||||
you run `import torch` anywhere else, the development version will be
 | 
			
		||||
used.
 | 
			
		||||
 | 
			
		||||
If you want to manage multiple builds of PyTorch, you can make use of
 | 
			
		||||
[conda environments](https://conda.io/docs/using/envs.html) to maintain
 | 
			
		||||
separate Python package environments, each of which can be tied to a
 | 
			
		||||
specific build of PyTorch.  To set one up:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
conda create -n pytorch-myfeature
 | 
			
		||||
source activate pytorch-myfeature
 | 
			
		||||
# if you run python now, torch will NOT be installed
 | 
			
		||||
python setup.py build develop
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## C++ Development tips
 | 
			
		||||
 | 
			
		||||
If you are working on the C++ code, there are a few important things that you
 | 
			
		||||
will want to keep in mind:
 | 
			
		||||
 | 
			
		||||
1. How to rebuild only the code you are working on, and
 | 
			
		||||
2. How to make rebuilds in the absence of changes go faster.
 | 
			
		||||
 | 
			
		||||
### Build only what you need.
 | 
			
		||||
 | 
			
		||||
`python setup.py build` will build everything, but since our build system is
 | 
			
		||||
not very optimized for incremental rebuilds, this will actually be very slow.
 | 
			
		||||
Far better is to only request rebuilds of the parts of the project you are
 | 
			
		||||
working on:
 | 
			
		||||
 | 
			
		||||
- Working on `torch/csrc`?  Run `python setup.py develop` to rebuild
 | 
			
		||||
  (NB: no `build` here!)
 | 
			
		||||
 | 
			
		||||
- Working on `torch/lib/TH`, did not make any cmake changes, and just want to
 | 
			
		||||
  see if it compiles?  Run `(cd torch/lib/build/TH && make install -j$(getconf _NPROCESSORS_ONLN))`.  This
 | 
			
		||||
  applies for any other subdirectory of `torch/lib`.  **Warning: Changes you
 | 
			
		||||
  make here will not be visible from Python.**  See below.
 | 
			
		||||
 | 
			
		||||
- Working on `torch/lib` and want to run your changes / rerun cmake?  Run
 | 
			
		||||
  `python setup.py build_deps`.  Note that this will rerun cmake for
 | 
			
		||||
  every subdirectory in TH; if you are only working on one project,
 | 
			
		||||
  consider editing `torch/lib/build_all.sh` and commenting out the
 | 
			
		||||
  `build` lines of libraries you are not working on.
 | 
			
		||||
 | 
			
		||||
On the initial build, you can also speed things up with the environment
 | 
			
		||||
variables `DEBUG` and `NO_CUDA`.
 | 
			
		||||
 | 
			
		||||
- `DEBUG=1` will enable debug builds (-g -O0)
 | 
			
		||||
- `NO_CUDA=1` will disable compiling CUDA (in case you are developing on something not CUDA related), to save compile time.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
```
 | 
			
		||||
NO_CUDA=1 DEBUG=1 python setup.py build develop
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Make sure you continue to pass these flags on subsequent builds.
 | 
			
		||||
 | 
			
		||||
### Make no-op build fast.
 | 
			
		||||
 | 
			
		||||
Python `setuptools` is pretty dumb, and always rebuilds every C file in a
 | 
			
		||||
project. Using ccache in a situation like this is a real time-saver. However, by
 | 
			
		||||
default, ccache does not properly support CUDA stuff, so here are the
 | 
			
		||||
instructions for installing a custom `ccache` fork that has CUDA support:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
# install and export ccache
 | 
			
		||||
if ! ls ~/ccache/bin/ccache
 | 
			
		||||
then
 | 
			
		||||
    sudo apt-get update
 | 
			
		||||
    sudo apt-get install -y automake autoconf
 | 
			
		||||
    sudo apt-get install -y asciidoc
 | 
			
		||||
    mkdir -p ~/ccache
 | 
			
		||||
    pushd /tmp
 | 
			
		||||
    rm -rf ccache
 | 
			
		||||
    git clone https://github.com/colesbury/ccache -b ccbin
 | 
			
		||||
    pushd ccache
 | 
			
		||||
    ./autogen.sh
 | 
			
		||||
    ./configure
 | 
			
		||||
    make install prefix=~/ccache
 | 
			
		||||
    popd
 | 
			
		||||
    popd
 | 
			
		||||
 | 
			
		||||
    mkdir -p ~/ccache/lib
 | 
			
		||||
    mkdir -p ~/ccache/cuda
 | 
			
		||||
    ln -s ~/ccache/bin/ccache ~/ccache/lib/cc
 | 
			
		||||
    ln -s ~/ccache/bin/ccache ~/ccache/lib/c++
 | 
			
		||||
    ln -s ~/ccache/bin/ccache ~/ccache/lib/gcc
 | 
			
		||||
    ln -s ~/ccache/bin/ccache ~/ccache/lib/g++
 | 
			
		||||
    ln -s ~/ccache/bin/ccache ~/ccache/cuda/nvcc
 | 
			
		||||
 | 
			
		||||
    ~/ccache/bin/ccache -M 25Gi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
export PATH=~/ccache/lib:$PATH
 | 
			
		||||
export CUDA_NVCC_EXECUTABLE=~/ccache/cuda/nvcc
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Hope this helps, and thanks for considering to contribute.
 | 
			
		||||
							
								
								
									
										36
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
FROM nvidia/cuda:8.0-cudnn6-devel-ubuntu16.04 
 | 
			
		||||
 | 
			
		||||
RUN echo "deb http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1604/x86_64 /" > /etc/apt/sources.list.d/nvidia-ml.list
 | 
			
		||||
 | 
			
		||||
RUN apt-get update && apt-get install -y --no-install-recommends \
 | 
			
		||||
         build-essential \
 | 
			
		||||
         cmake \
 | 
			
		||||
         git \
 | 
			
		||||
         curl \
 | 
			
		||||
         vim \
 | 
			
		||||
         ca-certificates \
 | 
			
		||||
         libjpeg-dev \
 | 
			
		||||
         libpng-dev &&\
 | 
			
		||||
     rm -rf /var/lib/apt/lists/*
 | 
			
		||||
 | 
			
		||||
RUN curl -o ~/miniconda.sh -O  https://repo.continuum.io/miniconda/Miniconda3-4.2.12-Linux-x86_64.sh  && \
 | 
			
		||||
     chmod +x ~/miniconda.sh && \
 | 
			
		||||
     ~/miniconda.sh -b -p /opt/conda && \     
 | 
			
		||||
     rm ~/miniconda.sh && \
 | 
			
		||||
     /opt/conda/bin/conda install conda-build && \
 | 
			
		||||
     /opt/conda/bin/conda create -y --name pytorch-py35 python=3.5.2 numpy pyyaml scipy ipython mkl&& \
 | 
			
		||||
     /opt/conda/bin/conda clean -ya 
 | 
			
		||||
ENV PATH /opt/conda/envs/pytorch-py35/bin:$PATH
 | 
			
		||||
RUN conda install --name pytorch-py35 -c soumith magma-cuda80
 | 
			
		||||
# This must be done before pip so that requirements.txt is available
 | 
			
		||||
WORKDIR /opt/pytorch
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
RUN TORCH_CUDA_ARCH_LIST="3.5 5.2 6.0 6.1+PTX" TORCH_NVCC_FLAGS="-Xfatbin -compress-all" \
 | 
			
		||||
    CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" \
 | 
			
		||||
    pip install -v .
 | 
			
		||||
 | 
			
		||||
RUN git clone https://github.com/pytorch/vision.git && cd vision && pip install -v .
 | 
			
		||||
 | 
			
		||||
WORKDIR /workspace
 | 
			
		||||
RUN chmod -R a+w /workspace
 | 
			
		||||
							
								
								
									
										38
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
Copyright (c) 2016-     Facebook, Inc            (Adam Paszke)
 | 
			
		||||
Copyright (c) 2014-     Facebook, Inc            (Soumith Chintala)
 | 
			
		||||
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
 | 
			
		||||
Copyright (c) 2012-2014 Deepmind Technologies    (Koray Kavukcuoglu)
 | 
			
		||||
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
 | 
			
		||||
Copyright (c) 2011-2013 NYU                      (Clement Farabet)
 | 
			
		||||
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
 | 
			
		||||
Copyright (c) 2006      Idiap Research Institute (Samy Bengio)
 | 
			
		||||
Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
 | 
			
		||||
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
1. Redistributions of source code must retain the above copyright
 | 
			
		||||
   notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
   notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
   documentation and/or other materials provided with the distribution.
 | 
			
		||||
 | 
			
		||||
3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America
 | 
			
		||||
   and IDIAP Research Institute nor the names of its contributors may be
 | 
			
		||||
   used to endorse or promote products derived from this software without
 | 
			
		||||
   specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | 
			
		||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
							
								
								
									
										258
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								README.md
									
									
									
									
									
								
							@ -1,32 +1,254 @@
 | 
			
		||||
# THNN
 | 
			
		||||
<p align="center"><img width="40%" src="docs/source/_static/img/pytorch-logo-dark.png" /></p>
 | 
			
		||||
 | 
			
		||||
THNN is a library that gathers nn's C implementations of neural network modules. It's entirely free of Lua dependency and therefore can be used in any application that has a C FFI. Please note that it only contains quite low level functions, and an object oriented C/C++ wrapper will be created soon as another library.
 | 
			
		||||
--------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
There is also a CUDA counterpart of THNN (THCUNN) in the [cunn repository](https://github.com/torch/cunn/tree/master/lib/THCUNN).
 | 
			
		||||
PyTorch is a Python package that provides two high-level features:
 | 
			
		||||
- Tensor computation (like NumPy) with strong GPU acceleration
 | 
			
		||||
- Deep neural networks built on a tape-based autograd system
 | 
			
		||||
 | 
			
		||||
## Links
 | 
			
		||||
You can reuse your favorite Python packages such as NumPy, SciPy and Cython to extend PyTorch when needed.
 | 
			
		||||
 | 
			
		||||
* [API reference](doc/api_reference.md)
 | 
			
		||||
* [Style guidelines](doc/style_guidelines.md)
 | 
			
		||||
We are in an early-release beta. Expect some adventures and rough edges.
 | 
			
		||||
 | 
			
		||||
## Motivation
 | 
			
		||||
- [More about PyTorch](#more-about-pytorch)
 | 
			
		||||
- [Installation](#installation)
 | 
			
		||||
  - [Binaries](#binaries)
 | 
			
		||||
  - [From Source](#from-source)
 | 
			
		||||
  - [Docker Image](#docker-image)
 | 
			
		||||
- [Getting Started](#getting-started)
 | 
			
		||||
- [Communication](#communication)
 | 
			
		||||
- [Releases and Contributing](#releases-and-contributing)
 | 
			
		||||
- [The Team](#the-team)
 | 
			
		||||
 | 
			
		||||
Torch's neural network package (nn) provided many optimized C implementations of modules, but the source files contained Lua specific code and headers so they couldn't be easily compiled and included anywhere else.
 | 
			
		||||
| System | 2.7 | 3.5 |
 | 
			
		||||
| --- | --- | --- |
 | 
			
		||||
| Linux CPU | [](https://travis-ci.org/pytorch/pytorch) | [](https://travis-ci.org/pytorch/pytorch) |
 | 
			
		||||
| Linux GPU | [](https://build.pytorch.org/job/pytorch-master-py2-linux) | [](https://build.pytorch.org/job/pytorch-master-py3-linux) |
 | 
			
		||||
| macOS CPU | [](https://build.pytorch.org/job/pytorch-master-py2-osx-cpu) | [](https://build.pytorch.org/job/pytorch-master-py3-osx-cpu) |
 | 
			
		||||
 | 
			
		||||
THNN is based on the same code, but is written in pure C, so it can be easily included in other code. **Future C implementations should be committed to THNN.**
 | 
			
		||||
 | 
			
		||||
## API
 | 
			
		||||
## More about PyTorch
 | 
			
		||||
 | 
			
		||||
THNN is a purely functional library. It provides 2-3 functions for each module, that perform the most important operations:
 | 
			
		||||
At a granular level, PyTorch is a library that consists of the following components:
 | 
			
		||||
 | 
			
		||||
* **updateOutput** - applies the module to an input
 | 
			
		||||
* **updateGradInput** - accepts gradient w.r.t. output and previous module input, and computes a gradient w.r.t. that input
 | 
			
		||||
* **accGradParameters** - *(optional, only modules with parameters)* accepts gradient w.r.t. output and previous module input, and computes gradient w.r.t. the parameters
 | 
			
		||||
<table>
 | 
			
		||||
<tr>
 | 
			
		||||
    <td><b> torch </b></td>
 | 
			
		||||
    <td> a Tensor library like NumPy, with strong GPU support </td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
    <td><b> torch.autograd </b></td>
 | 
			
		||||
    <td> a tape-based automatic differentiation library that supports all differentiable Tensor operations in torch </td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
    <td><b> torch.nn </b></td>
 | 
			
		||||
    <td> a neural networks library deeply integrated with autograd designed for maximum flexibility </td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
    <td><b> torch.multiprocessing  </b></td>
 | 
			
		||||
    <td> Python multiprocessing, but with magical memory sharing of torch Tensors across processes. Useful for data loading and Hogwild training. </td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
    <td><b> torch.utils </b></td>
 | 
			
		||||
    <td> DataLoader, Trainer and other utility functions for convenience </td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
    <td><b> torch.legacy(.nn/.optim) </b></td>
 | 
			
		||||
    <td> legacy code that has been ported over from torch for backward compatibility reasons </td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
For information on argument types please check the [API reference](doc/api_reference.md).
 | 
			
		||||
Usually one uses PyTorch either as:
 | 
			
		||||
 | 
			
		||||
## Developer docs
 | 
			
		||||
- a replacement for NumPy to use the power of GPUs.
 | 
			
		||||
- a deep learning research platform that provides maximum flexibility and speed
 | 
			
		||||
 | 
			
		||||
* [Style guidelines](doc/style_guidelines.md)
 | 
			
		||||
Elaborating further:
 | 
			
		||||
 | 
			
		||||
This section will be expanded when FFI refactoring will be finished.
 | 
			
		||||
### A GPU-Ready Tensor Library
 | 
			
		||||
 | 
			
		||||
If you use NumPy, then you have used Tensors (a.k.a ndarray).
 | 
			
		||||
 | 
			
		||||
<p align=center><img width="30%" src="docs/source/_static/img/tensor_illustration.png" /></p>
 | 
			
		||||
 | 
			
		||||
PyTorch provides Tensors that can live either on the CPU or the GPU, and accelerate
 | 
			
		||||
compute by a huge amount.
 | 
			
		||||
 | 
			
		||||
We provide a wide variety of tensor routines to accelerate and fit your scientific computation needs
 | 
			
		||||
such as slicing, indexing, math operations, linear algebra, reductions.
 | 
			
		||||
And they are fast!
 | 
			
		||||
 | 
			
		||||
### Dynamic Neural Networks: Tape-Based Autograd
 | 
			
		||||
 | 
			
		||||
PyTorch has a unique way of building neural networks: using and replaying a tape recorder.
 | 
			
		||||
 | 
			
		||||
Most frameworks such as TensorFlow, Theano, Caffe and CNTK have a static view of the world.
 | 
			
		||||
One has to build a neural network, and reuse the same structure again and again.
 | 
			
		||||
Changing the way the network behaves means that one has to start from scratch.
 | 
			
		||||
 | 
			
		||||
With PyTorch, we use a technique called reverse-mode auto-differentiation, which allows you to
 | 
			
		||||
change the way your network behaves arbitrarily with zero lag or overhead. Our inspiration comes
 | 
			
		||||
from several research papers on this topic, as well as current and past work such as
 | 
			
		||||
[autograd](https://github.com/twitter/torch-autograd),
 | 
			
		||||
[autograd](https://github.com/HIPS/autograd),
 | 
			
		||||
[Chainer](http://chainer.org), etc.
 | 
			
		||||
 | 
			
		||||
While this technique is not unique to PyTorch, it's one of the fastest implementations of it to date.
 | 
			
		||||
You get the best of speed and flexibility for your crazy research.
 | 
			
		||||
 | 
			
		||||
<p align=center><img width="80%" src="docs/source/_static/img/dynamic_graph.gif" /></p>
 | 
			
		||||
 | 
			
		||||
### Python First
 | 
			
		||||
 | 
			
		||||
PyTorch is not a Python binding into a monolithic C++ framework.
 | 
			
		||||
It is built to be deeply integrated into Python.
 | 
			
		||||
You can use it naturally like you would use NumPy / SciPy / scikit-learn etc.
 | 
			
		||||
You can write your new neural network layers in Python itself, using your favorite libraries
 | 
			
		||||
and use packages such as Cython and Numba.
 | 
			
		||||
Our goal is to not reinvent the wheel where appropriate.
 | 
			
		||||
 | 
			
		||||
### Imperative Experiences
 | 
			
		||||
 | 
			
		||||
PyTorch is designed to be intuitive, linear in thought and easy to use.
 | 
			
		||||
When you execute a line of code, it gets executed. There isn't an asynchronous view of the world.
 | 
			
		||||
When you drop into a debugger, or receive error messages and stack traces, understanding them is straightforward.
 | 
			
		||||
The stack trace points to exactly where your code was defined.
 | 
			
		||||
We hope you never spend hours debugging your code because of bad stack traces or asynchronous and opaque execution engines.
 | 
			
		||||
 | 
			
		||||
### Fast and Lean
 | 
			
		||||
 | 
			
		||||
PyTorch has minimal framework overhead. We integrate acceleration libraries
 | 
			
		||||
such as Intel MKL and NVIDIA (cuDNN, NCCL) to maximize speed.
 | 
			
		||||
At the core, its CPU and GPU Tensor and neural network backends
 | 
			
		||||
(TH, THC, THNN, THCUNN) are written as independent libraries with a C99 API.  
 | 
			
		||||
They are mature and have been tested for years.
 | 
			
		||||
 | 
			
		||||
Hence, PyTorch is quite fast – whether you run small or large neural networks.
 | 
			
		||||
 | 
			
		||||
The memory usage in PyTorch is extremely efficient compared to Torch or some of the alternatives.
 | 
			
		||||
We've written custom memory allocators for the GPU to make sure that
 | 
			
		||||
your deep learning models are maximally memory efficient.
 | 
			
		||||
This enables you to train bigger deep learning models than before.
 | 
			
		||||
 | 
			
		||||
### Extensions without Pain
 | 
			
		||||
 | 
			
		||||
Writing new neural network modules, or interfacing with PyTorch's Tensor API was designed to be straightforward
 | 
			
		||||
and with minimal abstractions.
 | 
			
		||||
 | 
			
		||||
You can write new neural network layers in Python using the torch API
 | 
			
		||||
[or your favorite NumPy-based libraries such as SciPy](http://pytorch.org/tutorials/advanced/numpy_extensions_tutorial.html).
 | 
			
		||||
 | 
			
		||||
If you want to write your layers in C/C++, we provide an extension API based on
 | 
			
		||||
[cffi](http://cffi.readthedocs.io/en/latest/) that is efficient and with minimal boilerplate.
 | 
			
		||||
There is no wrapper code that needs to be written. You can see [a tutorial here](http://pytorch.org/tutorials/advanced/c_extension.html) and [an example here](https://github.com/pytorch/extension-ffi).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
### Binaries
 | 
			
		||||
Commands to install from binaries via Conda or pip wheels are on our website:
 | 
			
		||||
 | 
			
		||||
[http://pytorch.org](http://pytorch.org)
 | 
			
		||||
 | 
			
		||||
### From Source
 | 
			
		||||
 | 
			
		||||
If you are installing from source, we highly recommend installing an [Anaconda](https://www.continuum.io/downloads) environment.
 | 
			
		||||
You will get a high-quality BLAS library (MKL) and you get a controlled compiler version regardless of your Linux distro.
 | 
			
		||||
 | 
			
		||||
Once you have [Anaconda](https://www.continuum.io/downloads) installed, here are the instructions.
 | 
			
		||||
 | 
			
		||||
If you want to compile with CUDA support, install
 | 
			
		||||
- [NVIDIA CUDA](https://developer.nvidia.com/cuda-downloads) 7.5 or above
 | 
			
		||||
- [NVIDIA cuDNN](https://developer.nvidia.com/cudnn) v5.x or above
 | 
			
		||||
 | 
			
		||||
If you want to disable CUDA support, export environment variable `NO_CUDA=1`.
 | 
			
		||||
 | 
			
		||||
#### Install optional dependencies
 | 
			
		||||
 | 
			
		||||
On Linux
 | 
			
		||||
```bash
 | 
			
		||||
export CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" # [anaconda root directory]
 | 
			
		||||
 | 
			
		||||
# Install basic dependencies
 | 
			
		||||
conda install numpy pyyaml mkl setuptools cmake gcc cffi
 | 
			
		||||
 | 
			
		||||
# Add LAPACK support for the GPU
 | 
			
		||||
conda install -c soumith magma-cuda80 # or magma-cuda75 if CUDA 7.5
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
On OSX
 | 
			
		||||
```bash
 | 
			
		||||
export CMAKE_PREFIX_PATH=[anaconda root directory]
 | 
			
		||||
conda install numpy pyyaml setuptools cmake cffi
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Install PyTorch
 | 
			
		||||
On Linux
 | 
			
		||||
```bash
 | 
			
		||||
python setup.py install
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
On OSX
 | 
			
		||||
```bash
 | 
			
		||||
MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py install
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Docker image
 | 
			
		||||
 | 
			
		||||
Dockerfile is supplied to build images with cuda support and cudnn v6. Build as usual
 | 
			
		||||
```
 | 
			
		||||
docker build -t pytorch .
 | 
			
		||||
```
 | 
			
		||||
Alternatively, if you want a runtime image, build with
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
docker build -t pytorch . -f tools/docker/Dockerfile_runtime
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
and run with nvidia-docker:
 | 
			
		||||
```
 | 
			
		||||
nvidia-docker run --rm -ti --ipc=host pytorch
 | 
			
		||||
```
 | 
			
		||||
Please note that PyTorch uses shared memory to share data between processes, so if torch multiprocessing is used (e.g.
 | 
			
		||||
for multithreaded data loaders) the default shared memory segment size that container runs with is not enough, and you
 | 
			
		||||
should increase shared memory size either with `--ipc=host` or `--shm-size` command line options to `nvidia-docker run`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Getting Started
 | 
			
		||||
 | 
			
		||||
Three pointers to get you started:
 | 
			
		||||
- [Tutorials: get you started with understanding and using PyTorch](http://pytorch.org/tutorials/)
 | 
			
		||||
- [Examples: easy to understand pytorch code across all domains](https://github.com/pytorch/examples)
 | 
			
		||||
- The API Reference: [http://pytorch.org/docs/](http://pytorch.org/docs/)
 | 
			
		||||
 | 
			
		||||
## Communication
 | 
			
		||||
* forums: discuss implementations, research, etc. http://discuss.pytorch.org
 | 
			
		||||
* GitHub issues: bug reports, feature requests, install issues, RFCs, thoughts, etc.
 | 
			
		||||
* Slack: general chat, online discussions, collaboration etc. https://pytorch.slack.com/ . If you need a slack invite, ping us at soumith@pytorch.org
 | 
			
		||||
* newsletter: no-noise, one-way email newsletter with important announcements about pytorch. You can sign-up here: http://eepurl.com/cbG0rv
 | 
			
		||||
 | 
			
		||||
## Releases and Contributing
 | 
			
		||||
 | 
			
		||||
PyTorch has a 90 day release cycle (major releases).
 | 
			
		||||
It's current state is Beta, we expect no obvious bugs. Please let us know if you encounter a bug by [filing an issue](https://github.com/pytorch/pytorch/issues).
 | 
			
		||||
 | 
			
		||||
We appreciate all contributions. If you are planning to contribute back bug-fixes, please do so without any further discussion.
 | 
			
		||||
 | 
			
		||||
If you plan to contribute new features, utility functions or extensions to the core, please first open an issue and discuss the feature with us.
 | 
			
		||||
Sending a PR without discussion might end up resulting in a rejected PR, because we might be taking the core in a different direction than you might be aware of.
 | 
			
		||||
 | 
			
		||||
**For the next release cycle, these are the 3 big features we are planning to add:**
 | 
			
		||||
 | 
			
		||||
1. [Distributed PyTorch](https://github.com/pytorch/pytorch/issues/241) (a draft implementation is present in this [branch](https://github.com/apaszke/pytorch-dist) )
 | 
			
		||||
2. Backward of Backward - Backpropagating through the optimization process itself. Some past and recent papers such as
 | 
			
		||||
   [Double Backprop](http://yann.lecun.com/exdb/publis/pdf/drucker-lecun-91.pdf) and [Unrolled GANs](https://arxiv.org/abs/1611.02163) need this.
 | 
			
		||||
3. Lazy Execution Engine for autograd - This will enable us to optionally introduce caching and JIT compilers to optimize autograd code.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## The Team
 | 
			
		||||
 | 
			
		||||
PyTorch is a community driven project with several skillful engineers and researchers contributing to it.
 | 
			
		||||
 | 
			
		||||
PyTorch is currently maintained by [Adam Paszke](https://apaszke.github.io/), [Sam Gross](https://github.com/colesbury) and [Soumith Chintala](http://soumith.ch) with major contributions coming from 10s of talented individuals in various forms and means. A non-exhaustive but growing list needs to mention: Sergey Zagoruyko, Adam Lerer, Francisco Massa, Andreas Kopf, James Bradbury, Zeming Lin, Yuandong Tian, Guillaume Lample, Marat Dukhan, Natalia Gimelshein.
 | 
			
		||||
 | 
			
		||||
Note: this project is unrelated to [hughperkins/pytorch](https://github.com/hughperkins/pytorch) with the same name. Hugh is a valuable contributor in the Torch community and has helped with many things Torch and PyTorch.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1885
									
								
								cmake/FindCUDA/FindCUDA.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1885
									
								
								cmake/FindCUDA/FindCUDA.cmake
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										106
									
								
								cmake/FindCUDA/FindCUDA/make2cmake.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								cmake/FindCUDA/FindCUDA/make2cmake.cmake
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,106 @@
 | 
			
		||||
#  James Bigler, NVIDIA Corp (nvidia.com - jbigler)
 | 
			
		||||
#  Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html
 | 
			
		||||
#
 | 
			
		||||
#  Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
#  Copyright (c) 2007-2009
 | 
			
		||||
#  Scientific Computing and Imaging Institute, University of Utah
 | 
			
		||||
#
 | 
			
		||||
#  This code is licensed under the MIT License.  See the FindCUDA.cmake script
 | 
			
		||||
#  for the text of the license.
 | 
			
		||||
 | 
			
		||||
# The MIT License
 | 
			
		||||
#
 | 
			
		||||
# License for the specific language governing rights and limitations under
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a
 | 
			
		||||
# copy of this software and associated documentation files (the "Software"),
 | 
			
		||||
# to deal in the Software without restriction, including without limitation
 | 
			
		||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
			
		||||
# and/or sell copies of the Software, and to permit persons to whom the
 | 
			
		||||
# Software is furnished to do so, subject to the following conditions:
 | 
			
		||||
#
 | 
			
		||||
# The above copyright notice and this permission notice shall be included
 | 
			
		||||
# in all copies or substantial portions of the Software.
 | 
			
		||||
#
 | 
			
		||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | 
			
		||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
			
		||||
# DEALINGS IN THE SOFTWARE.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#######################################################################
 | 
			
		||||
# This converts a file written in makefile syntax into one that can be included
 | 
			
		||||
# by CMake.
 | 
			
		||||
 | 
			
		||||
# Input variables
 | 
			
		||||
#
 | 
			
		||||
# verbose:BOOL=<>          OFF: Be as quiet as possible (default)
 | 
			
		||||
#                          ON : Extra output
 | 
			
		||||
#
 | 
			
		||||
# input_file:FILEPATH=<>   Path to dependecy file in makefile format
 | 
			
		||||
#
 | 
			
		||||
# output_file:FILEPATH=<>  Path to file with dependencies in CMake readable variable
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
file(READ ${input_file} depend_text)
 | 
			
		||||
 | 
			
		||||
if (NOT "${depend_text}" STREQUAL "")
 | 
			
		||||
 | 
			
		||||
  # message("FOUND DEPENDS")
 | 
			
		||||
 | 
			
		||||
  string(REPLACE "\\ " " " depend_text ${depend_text})
 | 
			
		||||
 | 
			
		||||
  # This works for the nvcc -M generated dependency files.
 | 
			
		||||
  string(REGEX REPLACE "^.* : " "" depend_text ${depend_text})
 | 
			
		||||
  string(REGEX REPLACE "[ \\\\]*\n" ";" depend_text ${depend_text})
 | 
			
		||||
 | 
			
		||||
  set(dependency_list "")
 | 
			
		||||
 | 
			
		||||
  foreach(file ${depend_text})
 | 
			
		||||
 | 
			
		||||
    string(REGEX REPLACE "^ +" "" file ${file})
 | 
			
		||||
 | 
			
		||||
    # OK, now if we had a UNC path, nvcc has a tendency to only output the first '/'
 | 
			
		||||
    # instead of '//'.  Here we will test to see if the file exists, if it doesn't then
 | 
			
		||||
    # try to prepend another '/' to the path and test again.  If it still fails remove the
 | 
			
		||||
    # path.
 | 
			
		||||
 | 
			
		||||
    if(NOT EXISTS "${file}")
 | 
			
		||||
      if (EXISTS "/${file}")
 | 
			
		||||
        set(file "/${file}")
 | 
			
		||||
      else()
 | 
			
		||||
        if(verbose)
 | 
			
		||||
          message(WARNING " Removing non-existent dependency file: ${file}")
 | 
			
		||||
        endif()
 | 
			
		||||
        set(file "")
 | 
			
		||||
      endif()
 | 
			
		||||
    endif()
 | 
			
		||||
 | 
			
		||||
    # Make sure we check to see if we have a file, before asking if it is not a directory.
 | 
			
		||||
    # if(NOT IS_DIRECTORY "") will return TRUE.
 | 
			
		||||
    if(file AND NOT IS_DIRECTORY "${file}")
 | 
			
		||||
      # If softlinks start to matter, we should change this to REALPATH.  For now we need
 | 
			
		||||
      # to flatten paths, because nvcc can generate stuff like /bin/../include instead of
 | 
			
		||||
      # just /include.
 | 
			
		||||
      get_filename_component(file_absolute "${file}" ABSOLUTE)
 | 
			
		||||
      list(APPEND dependency_list "${file_absolute}")
 | 
			
		||||
    endif()
 | 
			
		||||
 | 
			
		||||
  endforeach()
 | 
			
		||||
 | 
			
		||||
else()
 | 
			
		||||
  # message("FOUND NO DEPENDS")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Remove the duplicate entries and sort them.
 | 
			
		||||
list(REMOVE_DUPLICATES dependency_list)
 | 
			
		||||
list(SORT dependency_list)
 | 
			
		||||
 | 
			
		||||
foreach(file ${dependency_list})
 | 
			
		||||
  set(cuda_nvcc_depend "${cuda_nvcc_depend} \"${file}\"\n")
 | 
			
		||||
endforeach()
 | 
			
		||||
 | 
			
		||||
file(WRITE ${output_file} "# Generated by: make2cmake.cmake\nSET(CUDA_NVCC_DEPEND\n ${cuda_nvcc_depend})\n\n")
 | 
			
		||||
							
								
								
									
										111
									
								
								cmake/FindCUDA/FindCUDA/parse_cubin.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								cmake/FindCUDA/FindCUDA/parse_cubin.cmake
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,111 @@
 | 
			
		||||
#  James Bigler, NVIDIA Corp (nvidia.com - jbigler)
 | 
			
		||||
#  Abe Stephens, SCI Institute -- http://www.sci.utah.edu/~abe/FindCuda.html
 | 
			
		||||
#
 | 
			
		||||
#  Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
#  Copyright (c) 2007-2009
 | 
			
		||||
#  Scientific Computing and Imaging Institute, University of Utah
 | 
			
		||||
#
 | 
			
		||||
#  This code is licensed under the MIT License.  See the FindCUDA.cmake script
 | 
			
		||||
#  for the text of the license.
 | 
			
		||||
 | 
			
		||||
# The MIT License
 | 
			
		||||
#
 | 
			
		||||
# License for the specific language governing rights and limitations under
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a
 | 
			
		||||
# copy of this software and associated documentation files (the "Software"),
 | 
			
		||||
# to deal in the Software without restriction, including without limitation
 | 
			
		||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
			
		||||
# and/or sell copies of the Software, and to permit persons to whom the
 | 
			
		||||
# Software is furnished to do so, subject to the following conditions:
 | 
			
		||||
#
 | 
			
		||||
# The above copyright notice and this permission notice shall be included
 | 
			
		||||
# in all copies or substantial portions of the Software.
 | 
			
		||||
#
 | 
			
		||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | 
			
		||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
			
		||||
# DEALINGS IN THE SOFTWARE.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#######################################################################
 | 
			
		||||
# Parses a .cubin file produced by nvcc and reports statistics about the file.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
file(READ ${input_file} file_text)
 | 
			
		||||
 | 
			
		||||
if (NOT "${file_text}" STREQUAL "")
 | 
			
		||||
 | 
			
		||||
  string(REPLACE ";" "\\;" file_text ${file_text})
 | 
			
		||||
  string(REPLACE "\ncode" ";code" file_text ${file_text})
 | 
			
		||||
 | 
			
		||||
  list(LENGTH file_text len)
 | 
			
		||||
 | 
			
		||||
  foreach(line ${file_text})
 | 
			
		||||
 | 
			
		||||
    # Only look at "code { }" blocks.
 | 
			
		||||
    if(line MATCHES "^code")
 | 
			
		||||
 | 
			
		||||
      # Break into individual lines.
 | 
			
		||||
      string(REGEX REPLACE "\n" ";" line ${line})
 | 
			
		||||
 | 
			
		||||
      foreach(entry ${line})
 | 
			
		||||
 | 
			
		||||
        # Extract kernel names.
 | 
			
		||||
        if (${entry} MATCHES "[^g]name = ([^ ]+)")
 | 
			
		||||
          set(entry "${CMAKE_MATCH_1}")
 | 
			
		||||
 | 
			
		||||
          # Check to see if the kernel name starts with "_"
 | 
			
		||||
          set(skip FALSE)
 | 
			
		||||
          # if (${entry} MATCHES "^_")
 | 
			
		||||
            # Skip the rest of this block.
 | 
			
		||||
            # message("Skipping ${entry}")
 | 
			
		||||
            # set(skip TRUE)
 | 
			
		||||
          # else ()
 | 
			
		||||
            message("Kernel:    ${entry}")
 | 
			
		||||
          # endif ()
 | 
			
		||||
 | 
			
		||||
        endif()
 | 
			
		||||
 | 
			
		||||
        # Skip the rest of the block if necessary
 | 
			
		||||
        if(NOT skip)
 | 
			
		||||
 | 
			
		||||
          # Registers
 | 
			
		||||
          if (${entry} MATCHES "reg([ ]+)=([ ]+)([^ ]+)")
 | 
			
		||||
            set(entry "${CMAKE_MATCH_3}")
 | 
			
		||||
            message("Registers: ${entry}")
 | 
			
		||||
          endif()
 | 
			
		||||
 | 
			
		||||
          # Local memory
 | 
			
		||||
          if (${entry} MATCHES "lmem([ ]+)=([ ]+)([^ ]+)")
 | 
			
		||||
            set(entry "${CMAKE_MATCH_3}")
 | 
			
		||||
            message("Local:     ${entry}")
 | 
			
		||||
          endif()
 | 
			
		||||
 | 
			
		||||
          # Shared memory
 | 
			
		||||
          if (${entry} MATCHES "smem([ ]+)=([ ]+)([^ ]+)")
 | 
			
		||||
            set(entry "${CMAKE_MATCH_3}")
 | 
			
		||||
            message("Shared:    ${entry}")
 | 
			
		||||
          endif()
 | 
			
		||||
 | 
			
		||||
          if (${entry} MATCHES "^}")
 | 
			
		||||
            message("")
 | 
			
		||||
          endif()
 | 
			
		||||
 | 
			
		||||
        endif()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      endforeach()
 | 
			
		||||
 | 
			
		||||
    endif()
 | 
			
		||||
 | 
			
		||||
  endforeach()
 | 
			
		||||
 | 
			
		||||
else()
 | 
			
		||||
  # message("FOUND NO DEPENDS")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										291
									
								
								cmake/FindCUDA/FindCUDA/run_nvcc.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								cmake/FindCUDA/FindCUDA/run_nvcc.cmake
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,291 @@
 | 
			
		||||
#  James Bigler, NVIDIA Corp (nvidia.com - jbigler)
 | 
			
		||||
#
 | 
			
		||||
#  Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
#  This code is licensed under the MIT License.  See the FindCUDA.cmake script
 | 
			
		||||
#  for the text of the license.
 | 
			
		||||
 | 
			
		||||
# The MIT License
 | 
			
		||||
#
 | 
			
		||||
# License for the specific language governing rights and limitations under
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a
 | 
			
		||||
# copy of this software and associated documentation files (the "Software"),
 | 
			
		||||
# to deal in the Software without restriction, including without limitation
 | 
			
		||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
			
		||||
# and/or sell copies of the Software, and to permit persons to whom the
 | 
			
		||||
# Software is furnished to do so, subject to the following conditions:
 | 
			
		||||
#
 | 
			
		||||
# The above copyright notice and this permission notice shall be included
 | 
			
		||||
# in all copies or substantial portions of the Software.
 | 
			
		||||
#
 | 
			
		||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | 
			
		||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
			
		||||
# DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##########################################################################
 | 
			
		||||
# This file runs the nvcc commands to produce the desired output file along with
 | 
			
		||||
# the dependency file needed by CMake to compute dependencies.  In addition the
 | 
			
		||||
# file checks the output of each command and if the command fails it deletes the
 | 
			
		||||
# output files.
 | 
			
		||||
 | 
			
		||||
# Input variables
 | 
			
		||||
#
 | 
			
		||||
# verbose:BOOL=<>          OFF: Be as quiet as possible (default)
 | 
			
		||||
#                          ON : Describe each step
 | 
			
		||||
#
 | 
			
		||||
# build_configuration:STRING=<> Typically one of Debug, MinSizeRel, Release, or
 | 
			
		||||
#                               RelWithDebInfo, but it should match one of the
 | 
			
		||||
#                               entries in CUDA_HOST_FLAGS. This is the build
 | 
			
		||||
#                               configuration used when compiling the code.  If
 | 
			
		||||
#                               blank or unspecified Debug is assumed as this is
 | 
			
		||||
#                               what CMake does.
 | 
			
		||||
#
 | 
			
		||||
# generated_file:STRING=<> File to generate.  This argument must be passed in.
 | 
			
		||||
#
 | 
			
		||||
# generated_cubin_file:STRING=<> File to generate.  This argument must be passed
 | 
			
		||||
#                                                   in if build_cubin is true.
 | 
			
		||||
 | 
			
		||||
if(NOT generated_file)
 | 
			
		||||
  message(FATAL_ERROR "You must specify generated_file on the command line")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Set these up as variables to make reading the generated file easier
 | 
			
		||||
set(CMAKE_COMMAND "@CMAKE_COMMAND@") # path
 | 
			
		||||
set(source_file "@source_file@") # path
 | 
			
		||||
set(NVCC_generated_dependency_file "@NVCC_generated_dependency_file@") # path
 | 
			
		||||
set(cmake_dependency_file "@cmake_dependency_file@") # path
 | 
			
		||||
set(CUDA_make2cmake "@CUDA_make2cmake@") # path
 | 
			
		||||
set(CUDA_parse_cubin "@CUDA_parse_cubin@") # path
 | 
			
		||||
set(build_cubin @build_cubin@) # bool
 | 
			
		||||
set(CUDA_HOST_COMPILER "@CUDA_HOST_COMPILER@") # path
 | 
			
		||||
# We won't actually use these variables for now, but we need to set this, in
 | 
			
		||||
# order to force this file to be run again if it changes.
 | 
			
		||||
set(generated_file_path "@generated_file_path@") # path
 | 
			
		||||
set(generated_file_internal "@generated_file@") # path
 | 
			
		||||
set(generated_cubin_file_internal "@generated_cubin_file@") # path
 | 
			
		||||
 | 
			
		||||
set(CUDA_NVCC_EXECUTABLE "@CUDA_NVCC_EXECUTABLE@") # path
 | 
			
		||||
set(CUDA_NVCC_FLAGS @CUDA_NVCC_FLAGS@ ;; @CUDA_WRAP_OPTION_NVCC_FLAGS@) # list
 | 
			
		||||
@CUDA_NVCC_FLAGS_CONFIG@
 | 
			
		||||
set(nvcc_flags @nvcc_flags@) # list
 | 
			
		||||
set(CUDA_NVCC_INCLUDE_ARGS "@CUDA_NVCC_INCLUDE_ARGS@") # list (needs to be in quotes to handle spaces properly).
 | 
			
		||||
set(format_flag "@format_flag@") # string
 | 
			
		||||
set(cuda_language_flag @cuda_language_flag@) # list
 | 
			
		||||
 | 
			
		||||
if(build_cubin AND NOT generated_cubin_file)
 | 
			
		||||
  message(FATAL_ERROR "You must specify generated_cubin_file on the command line")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# This is the list of host compilation flags.  It C or CXX should already have
 | 
			
		||||
# been chosen by FindCUDA.cmake.
 | 
			
		||||
@CUDA_HOST_FLAGS@
 | 
			
		||||
 | 
			
		||||
# Take the compiler flags and package them up to be sent to the compiler via -Xcompiler
 | 
			
		||||
set(nvcc_host_compiler_flags "")
 | 
			
		||||
# If we weren't given a build_configuration, use Debug.
 | 
			
		||||
if(NOT build_configuration)
 | 
			
		||||
  set(build_configuration Debug)
 | 
			
		||||
endif()
 | 
			
		||||
string(TOUPPER "${build_configuration}" build_configuration)
 | 
			
		||||
#message("CUDA_NVCC_HOST_COMPILER_FLAGS = ${CUDA_NVCC_HOST_COMPILER_FLAGS}")
 | 
			
		||||
foreach(flag ${CMAKE_HOST_FLAGS} ${CMAKE_HOST_FLAGS_${build_configuration}})
 | 
			
		||||
  # Extra quotes are added around each flag to help nvcc parse out flags with spaces.
 | 
			
		||||
  set(nvcc_host_compiler_flags "${nvcc_host_compiler_flags},\"${flag}\"")
 | 
			
		||||
endforeach()
 | 
			
		||||
if (nvcc_host_compiler_flags)
 | 
			
		||||
  set(nvcc_host_compiler_flags "-Xcompiler" ${nvcc_host_compiler_flags})
 | 
			
		||||
endif()
 | 
			
		||||
#message("nvcc_host_compiler_flags = \"${nvcc_host_compiler_flags}\"")
 | 
			
		||||
# Add the build specific configuration flags
 | 
			
		||||
list(APPEND CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS_${build_configuration}})
 | 
			
		||||
 | 
			
		||||
# Any -ccbin existing in CUDA_NVCC_FLAGS gets highest priority
 | 
			
		||||
list( FIND CUDA_NVCC_FLAGS "-ccbin" ccbin_found0 )
 | 
			
		||||
list( FIND CUDA_NVCC_FLAGS "--compiler-bindir" ccbin_found1 )
 | 
			
		||||
if( ccbin_found0 LESS 0 AND ccbin_found1 LESS 0 AND CUDA_HOST_COMPILER )
 | 
			
		||||
  if (CUDA_HOST_COMPILER STREQUAL "$(VCInstallDir)bin" AND DEFINED CCBIN)
 | 
			
		||||
    set(CCBIN -ccbin "${CCBIN}")
 | 
			
		||||
  else()
 | 
			
		||||
    set(CCBIN -ccbin "${CUDA_HOST_COMPILER}")
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# cuda_execute_process - Executes a command with optional command echo and status message.
 | 
			
		||||
#
 | 
			
		||||
#   status  - Status message to print if verbose is true
 | 
			
		||||
#   command - COMMAND argument from the usual execute_process argument structure
 | 
			
		||||
#   ARGN    - Remaining arguments are the command with arguments
 | 
			
		||||
#
 | 
			
		||||
#   CUDA_result - return value from running the command
 | 
			
		||||
#
 | 
			
		||||
# Make this a macro instead of a function, so that things like RESULT_VARIABLE
 | 
			
		||||
# and other return variables are present after executing the process.
 | 
			
		||||
macro(cuda_execute_process status command)
 | 
			
		||||
  set(_command ${command})
 | 
			
		||||
  if(NOT "x${_command}" STREQUAL "xCOMMAND")
 | 
			
		||||
    message(FATAL_ERROR "Malformed call to cuda_execute_process.  Missing COMMAND as second argument. (command = ${command})")
 | 
			
		||||
  endif()
 | 
			
		||||
  if(verbose)
 | 
			
		||||
    execute_process(COMMAND "${CMAKE_COMMAND}" -E echo -- ${status})
 | 
			
		||||
    # Now we need to build up our command string.  We are accounting for quotes
 | 
			
		||||
    # and spaces, anything else is left up to the user to fix if they want to
 | 
			
		||||
    # copy and paste a runnable command line.
 | 
			
		||||
    set(cuda_execute_process_string)
 | 
			
		||||
    foreach(arg ${ARGN})
 | 
			
		||||
      # If there are quotes, excape them, so they come through.
 | 
			
		||||
      string(REPLACE "\"" "\\\"" arg ${arg})
 | 
			
		||||
      # Args with spaces need quotes around them to get them to be parsed as a single argument.
 | 
			
		||||
      if(arg MATCHES " ")
 | 
			
		||||
        list(APPEND cuda_execute_process_string "\"${arg}\"")
 | 
			
		||||
      else()
 | 
			
		||||
        list(APPEND cuda_execute_process_string ${arg})
 | 
			
		||||
      endif()
 | 
			
		||||
    endforeach()
 | 
			
		||||
    # Echo the command
 | 
			
		||||
    execute_process(COMMAND ${CMAKE_COMMAND} -E echo ${cuda_execute_process_string})
 | 
			
		||||
  endif()
 | 
			
		||||
  # Run the command
 | 
			
		||||
  execute_process(COMMAND ${ARGN} RESULT_VARIABLE CUDA_result )
 | 
			
		||||
endmacro()
 | 
			
		||||
 | 
			
		||||
# Delete the target file
 | 
			
		||||
cuda_execute_process(
 | 
			
		||||
  "Removing ${generated_file}"
 | 
			
		||||
  COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}"
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
# For CUDA 2.3 and below, -G -M doesn't work, so remove the -G flag
 | 
			
		||||
# for dependency generation and hope for the best.
 | 
			
		||||
set(depends_CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS}")
 | 
			
		||||
set(CUDA_VERSION @CUDA_VERSION@)
 | 
			
		||||
if(CUDA_VERSION VERSION_LESS "3.0")
 | 
			
		||||
  cmake_policy(PUSH)
 | 
			
		||||
  # CMake policy 0007 NEW states that empty list elements are not
 | 
			
		||||
  # ignored.  I'm just setting it to avoid the warning that's printed.
 | 
			
		||||
  cmake_policy(SET CMP0007 NEW)
 | 
			
		||||
  # Note that this will remove all occurances of -G.
 | 
			
		||||
  list(REMOVE_ITEM depends_CUDA_NVCC_FLAGS "-G")
 | 
			
		||||
  cmake_policy(POP)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# nvcc doesn't define __CUDACC__ for some reason when generating dependency files.  This
 | 
			
		||||
# can cause incorrect dependencies when #including files based on this macro which is
 | 
			
		||||
# defined in the generating passes of nvcc invokation.  We will go ahead and manually
 | 
			
		||||
# define this for now until a future version fixes this bug.
 | 
			
		||||
set(CUDACC_DEFINE -D__CUDACC__)
 | 
			
		||||
 | 
			
		||||
# Generate the dependency file
 | 
			
		||||
cuda_execute_process(
 | 
			
		||||
  "Generating dependency file: ${NVCC_generated_dependency_file}"
 | 
			
		||||
  COMMAND "${CUDA_NVCC_EXECUTABLE}"
 | 
			
		||||
  -M
 | 
			
		||||
  ${CUDACC_DEFINE}
 | 
			
		||||
  "${source_file}"
 | 
			
		||||
  -o "${NVCC_generated_dependency_file}"
 | 
			
		||||
  ${CCBIN}
 | 
			
		||||
  ${nvcc_flags}
 | 
			
		||||
  ${nvcc_host_compiler_flags}
 | 
			
		||||
  ${depends_CUDA_NVCC_FLAGS}
 | 
			
		||||
  -DNVCC
 | 
			
		||||
  ${CUDA_NVCC_INCLUDE_ARGS}
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
if(CUDA_result)
 | 
			
		||||
  message(FATAL_ERROR "Error generating ${generated_file}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Generate the cmake readable dependency file to a temp file.  Don't put the
 | 
			
		||||
# quotes just around the filenames for the input_file and output_file variables.
 | 
			
		||||
# CMake will pass the quotes through and not be able to find the file.
 | 
			
		||||
cuda_execute_process(
 | 
			
		||||
  "Generating temporary cmake readable file: ${cmake_dependency_file}.tmp"
 | 
			
		||||
  COMMAND "${CMAKE_COMMAND}"
 | 
			
		||||
  -D "input_file:FILEPATH=${NVCC_generated_dependency_file}"
 | 
			
		||||
  -D "output_file:FILEPATH=${cmake_dependency_file}.tmp"
 | 
			
		||||
  -D "verbose=${verbose}"
 | 
			
		||||
  -P "${CUDA_make2cmake}"
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
if(CUDA_result)
 | 
			
		||||
  message(FATAL_ERROR "Error generating ${generated_file}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Copy the file if it is different
 | 
			
		||||
cuda_execute_process(
 | 
			
		||||
  "Copy if different ${cmake_dependency_file}.tmp to ${cmake_dependency_file}"
 | 
			
		||||
  COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${cmake_dependency_file}.tmp" "${cmake_dependency_file}"
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
if(CUDA_result)
 | 
			
		||||
  message(FATAL_ERROR "Error generating ${generated_file}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Delete the temporary file
 | 
			
		||||
cuda_execute_process(
 | 
			
		||||
  "Removing ${cmake_dependency_file}.tmp and ${NVCC_generated_dependency_file}"
 | 
			
		||||
  COMMAND "${CMAKE_COMMAND}" -E remove "${cmake_dependency_file}.tmp" "${NVCC_generated_dependency_file}"
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
if(CUDA_result)
 | 
			
		||||
  message(FATAL_ERROR "Error generating ${generated_file}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Generate the code
 | 
			
		||||
cuda_execute_process(
 | 
			
		||||
  "Generating ${generated_file}"
 | 
			
		||||
  COMMAND "${CUDA_NVCC_EXECUTABLE}"
 | 
			
		||||
  "${source_file}"
 | 
			
		||||
  ${cuda_language_flag}
 | 
			
		||||
  ${format_flag} -o "${generated_file}"
 | 
			
		||||
  ${CCBIN}
 | 
			
		||||
  ${nvcc_flags}
 | 
			
		||||
  ${nvcc_host_compiler_flags}
 | 
			
		||||
  ${CUDA_NVCC_FLAGS}
 | 
			
		||||
  -DNVCC
 | 
			
		||||
  ${CUDA_NVCC_INCLUDE_ARGS}
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
if(CUDA_result)
 | 
			
		||||
  # Since nvcc can sometimes leave half done files make sure that we delete the output file.
 | 
			
		||||
  cuda_execute_process(
 | 
			
		||||
    "Removing ${generated_file}"
 | 
			
		||||
    COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}"
 | 
			
		||||
    )
 | 
			
		||||
  message(FATAL_ERROR "Error generating file ${generated_file}")
 | 
			
		||||
else()
 | 
			
		||||
  if(verbose)
 | 
			
		||||
    message("Generated ${generated_file} successfully.")
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Cubin resource report commands.
 | 
			
		||||
if( build_cubin )
 | 
			
		||||
  # Run with -cubin to produce resource usage report.
 | 
			
		||||
  cuda_execute_process(
 | 
			
		||||
    "Generating ${generated_cubin_file}"
 | 
			
		||||
    COMMAND "${CUDA_NVCC_EXECUTABLE}"
 | 
			
		||||
    "${source_file}"
 | 
			
		||||
    ${CUDA_NVCC_FLAGS}
 | 
			
		||||
    ${nvcc_flags}
 | 
			
		||||
    ${CCBIN}
 | 
			
		||||
    ${nvcc_host_compiler_flags}
 | 
			
		||||
    -DNVCC
 | 
			
		||||
    -cubin
 | 
			
		||||
    -o "${generated_cubin_file}"
 | 
			
		||||
    ${CUDA_NVCC_INCLUDE_ARGS}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  # Execute the parser script.
 | 
			
		||||
  cuda_execute_process(
 | 
			
		||||
    "Executing the parser script"
 | 
			
		||||
    COMMAND  "${CMAKE_COMMAND}"
 | 
			
		||||
    -D "input_file:STRING=${generated_cubin_file}"
 | 
			
		||||
    -P "${CUDA_parse_cubin}"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
endif()
 | 
			
		||||
							
								
								
									
										200
									
								
								cmake/FindCUDA/FindCUDA/select_compute_arch.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								cmake/FindCUDA/FindCUDA/select_compute_arch.cmake
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,200 @@
 | 
			
		||||
# Synopsis:
 | 
			
		||||
#   CUDA_SELECT_NVCC_ARCH_FLAGS(out_variable [target_CUDA_architectures])
 | 
			
		||||
#   -- Selects GPU arch flags for nvcc based on target_CUDA_architectures
 | 
			
		||||
#      target_CUDA_architectures : Auto | Common | All | LIST(ARCH_AND_PTX ...)
 | 
			
		||||
#       - "Auto" detects local machine GPU compute arch at runtime.
 | 
			
		||||
#       - "Common" and "All" cover common and entire subsets of architectures
 | 
			
		||||
#      ARCH_AND_PTX : NAME | NUM.NUM | NUM.NUM(NUM.NUM) | NUM.NUM+PTX
 | 
			
		||||
#      NAME: Fermi Kepler Maxwell Kepler+Tegra Kepler+Tesla Maxwell+Tegra Pascal
 | 
			
		||||
#      NUM: Any number. Only those pairs are currently accepted by NVCC though:
 | 
			
		||||
#            2.0 2.1 3.0 3.2 3.5 3.7 5.0 5.2 5.3 6.0 6.2
 | 
			
		||||
#      Returns LIST of flags to be added to CUDA_NVCC_FLAGS in ${out_variable}
 | 
			
		||||
#      Additionally, sets ${out_variable}_readable to the resulting numeric list
 | 
			
		||||
#      Example:
 | 
			
		||||
#       CUDA_SELECT_NVCC_ARCH_FLAGS(ARCH_FLAGS 3.0 3.5+PTX 5.2(5.0) Maxwell)
 | 
			
		||||
#        LIST(APPEND CUDA_NVCC_FLAGS ${ARCH_FLAGS})
 | 
			
		||||
#
 | 
			
		||||
#      More info on CUDA architectures: https://en.wikipedia.org/wiki/CUDA
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# This list will be used for CUDA_ARCH_NAME = All option
 | 
			
		||||
set(CUDA_KNOWN_GPU_ARCHITECTURES  "Fermi" "Kepler" "Maxwell")
 | 
			
		||||
 | 
			
		||||
# This list will be used for CUDA_ARCH_NAME = Common option (enabled by default)
 | 
			
		||||
set(CUDA_COMMON_GPU_ARCHITECTURES "3.0" "3.5" "5.0")
 | 
			
		||||
 | 
			
		||||
if (CUDA_VERSION VERSION_GREATER "6.5")
 | 
			
		||||
  list(APPEND CUDA_KNOWN_GPU_ARCHITECTURES "Kepler+Tegra" "Kepler+Tesla" "Maxwell+Tegra")
 | 
			
		||||
  list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "5.2")
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
if (CUDA_VERSION VERSION_GREATER "7.5")
 | 
			
		||||
  list(APPEND CUDA_KNOWN_GPU_ARCHITECTURES "Pascal")
 | 
			
		||||
  list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "6.0" "6.1" "6.1+PTX")
 | 
			
		||||
else()
 | 
			
		||||
  list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "5.2+PTX")
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
################################################################################################
 | 
			
		||||
# A function for automatic detection of GPUs installed  (if autodetection is enabled)
 | 
			
		||||
# Usage:
 | 
			
		||||
#   CUDA_DETECT_INSTALLED_GPUS(OUT_VARIABLE)
 | 
			
		||||
#
 | 
			
		||||
function(CUDA_DETECT_INSTALLED_GPUS OUT_VARIABLE)
 | 
			
		||||
  if(NOT CUDA_GPU_DETECT_OUTPUT)
 | 
			
		||||
    set(cufile ${PROJECT_BINARY_DIR}/detect_cuda_archs.cu)
 | 
			
		||||
 | 
			
		||||
    file(WRITE ${cufile} ""
 | 
			
		||||
      "#include <cstdio>\n"
 | 
			
		||||
      "int main()\n"
 | 
			
		||||
      "{\n"
 | 
			
		||||
      "  int count = 0;\n"
 | 
			
		||||
      "  if (cudaSuccess != cudaGetDeviceCount(&count)) return -1;\n"
 | 
			
		||||
      "  if (count == 0) return -1;\n"
 | 
			
		||||
      "  for (int device = 0; device < count; ++device)\n"
 | 
			
		||||
      "  {\n"
 | 
			
		||||
      "    cudaDeviceProp prop;\n"
 | 
			
		||||
      "    if (cudaSuccess == cudaGetDeviceProperties(&prop, device))\n"
 | 
			
		||||
      "      std::printf(\"%d.%d \", prop.major, prop.minor);\n"
 | 
			
		||||
      "  }\n"
 | 
			
		||||
      "  return 0;\n"
 | 
			
		||||
      "}\n")
 | 
			
		||||
 | 
			
		||||
    execute_process(COMMAND "${CUDA_NVCC_EXECUTABLE}" "--run" "${cufile}"
 | 
			
		||||
                    "-ccbin" ${CMAKE_CXX_COMPILER}
 | 
			
		||||
                    WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/CMakeFiles/"
 | 
			
		||||
                    RESULT_VARIABLE nvcc_res OUTPUT_VARIABLE nvcc_out
 | 
			
		||||
                    ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
 | 
			
		||||
 | 
			
		||||
    if(nvcc_res EQUAL 0)
 | 
			
		||||
      # only keep the last line of nvcc_out
 | 
			
		||||
      STRING(REGEX REPLACE ";" "\\\\;" nvcc_out "${nvcc_out}")
 | 
			
		||||
      STRING(REGEX REPLACE "\n" ";" nvcc_out "${nvcc_out}")
 | 
			
		||||
      list(GET nvcc_out -1 nvcc_out)
 | 
			
		||||
      string(REPLACE "2.1" "2.1(2.0)" nvcc_out "${nvcc_out}")
 | 
			
		||||
      set(CUDA_GPU_DETECT_OUTPUT ${nvcc_out} CACHE INTERNAL "Returned GPU architetures from detect_gpus tool" FORCE)
 | 
			
		||||
    endif()
 | 
			
		||||
  endif()
 | 
			
		||||
 | 
			
		||||
  if(NOT CUDA_GPU_DETECT_OUTPUT)
 | 
			
		||||
    message(STATUS "Automatic GPU detection failed. Building for common architectures.")
 | 
			
		||||
    set(${OUT_VARIABLE} ${CUDA_COMMON_GPU_ARCHITECTURES} PARENT_SCOPE)
 | 
			
		||||
  else()
 | 
			
		||||
    set(${OUT_VARIABLE} ${CUDA_GPU_DETECT_OUTPUT} PARENT_SCOPE)
 | 
			
		||||
  endif()
 | 
			
		||||
endfunction()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
################################################################################################
 | 
			
		||||
# Function for selecting GPU arch flags for nvcc based on CUDA architectures from parameter list
 | 
			
		||||
# Usage:
 | 
			
		||||
#   SELECT_NVCC_ARCH_FLAGS(out_variable [list of CUDA compute archs])
 | 
			
		||||
function(CUDA_SELECT_NVCC_ARCH_FLAGS out_variable)
 | 
			
		||||
  set(CUDA_ARCH_LIST "${ARGN}")
 | 
			
		||||
 | 
			
		||||
  if("X${CUDA_ARCH_LIST}" STREQUAL "X" )
 | 
			
		||||
    set(CUDA_ARCH_LIST "Auto")
 | 
			
		||||
  endif()
 | 
			
		||||
 | 
			
		||||
  set(cuda_arch_bin)
 | 
			
		||||
  set(cuda_arch_ptx)
 | 
			
		||||
 | 
			
		||||
  if("${CUDA_ARCH_LIST}" STREQUAL "All")
 | 
			
		||||
    set(CUDA_ARCH_LIST ${CUDA_KNOWN_GPU_ARCHITECTURES})
 | 
			
		||||
  elseif("${CUDA_ARCH_LIST}" STREQUAL "Common")
 | 
			
		||||
    set(CUDA_ARCH_LIST ${CUDA_COMMON_GPU_ARCHITECTURES})
 | 
			
		||||
  elseif("${CUDA_ARCH_LIST}" STREQUAL "Auto")
 | 
			
		||||
    CUDA_DETECT_INSTALLED_GPUS(CUDA_ARCH_LIST)
 | 
			
		||||
    message(STATUS "Autodetected CUDA architecture(s): ${CUDA_ARCH_LIST}")
 | 
			
		||||
  endif()
 | 
			
		||||
 | 
			
		||||
  # Now process the list and look for names
 | 
			
		||||
  string(REGEX REPLACE "[ \t]+" ";" CUDA_ARCH_LIST "${CUDA_ARCH_LIST}")
 | 
			
		||||
  list(REMOVE_DUPLICATES CUDA_ARCH_LIST)
 | 
			
		||||
  foreach(arch_name ${CUDA_ARCH_LIST})
 | 
			
		||||
    set(arch_bin)
 | 
			
		||||
    set(add_ptx FALSE)
 | 
			
		||||
    # Check to see if we are compiling PTX
 | 
			
		||||
    if(arch_name MATCHES "(.*)\\+PTX$")
 | 
			
		||||
      set(add_ptx TRUE)
 | 
			
		||||
      set(arch_name ${CMAKE_MATCH_1})
 | 
			
		||||
    endif()
 | 
			
		||||
    if(arch_name MATCHES "(^[0-9]\\.[0-9](\\([0-9]\\.[0-9]\\))?)$")
 | 
			
		||||
      set(arch_bin ${CMAKE_MATCH_1})
 | 
			
		||||
      set(arch_ptx ${arch_bin})
 | 
			
		||||
    else()
 | 
			
		||||
      # Look for it in our list of known architectures
 | 
			
		||||
      if(${arch_name} STREQUAL "Fermi")
 | 
			
		||||
        set(arch_bin "2.0 2.1(2.0)")
 | 
			
		||||
      elseif(${arch_name} STREQUAL "Kepler+Tegra")
 | 
			
		||||
        set(arch_bin 3.2)
 | 
			
		||||
      elseif(${arch_name} STREQUAL "Kepler+Tesla")
 | 
			
		||||
        set(arch_bin 3.7)
 | 
			
		||||
      elseif(${arch_name} STREQUAL "Kepler")
 | 
			
		||||
        set(arch_bin 3.0 3.5)
 | 
			
		||||
        set(arch_ptx 3.5)
 | 
			
		||||
      elseif(${arch_name} STREQUAL "Maxwell+Tegra")
 | 
			
		||||
        set(arch_bin 5.3)
 | 
			
		||||
      elseif(${arch_name} STREQUAL "Maxwell")
 | 
			
		||||
        set(arch_bin 5.0 5.2)
 | 
			
		||||
        set(arch_ptx 5.2)
 | 
			
		||||
      elseif(${arch_name} STREQUAL "Pascal")
 | 
			
		||||
        set(arch_bin 6.0 6.1)
 | 
			
		||||
        set(arch_ptx 6.1)
 | 
			
		||||
      else()
 | 
			
		||||
        message(SEND_ERROR "Unknown CUDA Architecture Name ${arch_name} in CUDA_SELECT_NVCC_ARCH_FLAGS")
 | 
			
		||||
      endif()
 | 
			
		||||
    endif()
 | 
			
		||||
    if(NOT arch_bin)
 | 
			
		||||
      message(SEND_ERROR "arch_bin wasn't set for some reason")
 | 
			
		||||
    endif()
 | 
			
		||||
    list(APPEND cuda_arch_bin ${arch_bin})
 | 
			
		||||
    if(add_ptx)
 | 
			
		||||
      if (NOT arch_ptx)
 | 
			
		||||
        set(arch_ptx ${arch_bin})
 | 
			
		||||
      endif()
 | 
			
		||||
      list(APPEND cuda_arch_ptx ${arch_ptx})
 | 
			
		||||
    endif()
 | 
			
		||||
  endforeach()
 | 
			
		||||
 | 
			
		||||
  # remove dots and convert to lists
 | 
			
		||||
  string(REGEX REPLACE "\\." "" cuda_arch_bin "${cuda_arch_bin}")
 | 
			
		||||
  string(REGEX REPLACE "\\." "" cuda_arch_ptx "${cuda_arch_ptx}")
 | 
			
		||||
  string(REGEX MATCHALL "[0-9()]+" cuda_arch_bin "${cuda_arch_bin}")
 | 
			
		||||
  string(REGEX MATCHALL "[0-9]+"   cuda_arch_ptx "${cuda_arch_ptx}")
 | 
			
		||||
 | 
			
		||||
  if(cuda_arch_bin)
 | 
			
		||||
    list(REMOVE_DUPLICATES cuda_arch_bin)
 | 
			
		||||
  endif()
 | 
			
		||||
  if(cuda_arch_ptx)
 | 
			
		||||
    list(REMOVE_DUPLICATES cuda_arch_ptx)
 | 
			
		||||
  endif()
 | 
			
		||||
 | 
			
		||||
  set(nvcc_flags "")
 | 
			
		||||
  set(nvcc_archs_readable "")
 | 
			
		||||
 | 
			
		||||
  # Tell NVCC to add binaries for the specified GPUs
 | 
			
		||||
  foreach(arch ${cuda_arch_bin})
 | 
			
		||||
    if(arch MATCHES "([0-9]+)\\(([0-9]+)\\)")
 | 
			
		||||
      # User explicitly specified ARCH for the concrete CODE
 | 
			
		||||
      list(APPEND nvcc_flags -gencode arch=compute_${CMAKE_MATCH_2},code=sm_${CMAKE_MATCH_1})
 | 
			
		||||
      list(APPEND nvcc_archs_readable sm_${CMAKE_MATCH_1})
 | 
			
		||||
    else()
 | 
			
		||||
      # User didn't explicitly specify ARCH for the concrete CODE, we assume ARCH=CODE
 | 
			
		||||
      list(APPEND nvcc_flags -gencode arch=compute_${arch},code=sm_${arch})
 | 
			
		||||
      list(APPEND nvcc_archs_readable sm_${arch})
 | 
			
		||||
    endif()
 | 
			
		||||
  endforeach()
 | 
			
		||||
 | 
			
		||||
  # Tell NVCC to add PTX intermediate code for the specified architectures
 | 
			
		||||
  foreach(arch ${cuda_arch_ptx})
 | 
			
		||||
    list(APPEND nvcc_flags -gencode arch=compute_${arch},code=compute_${arch})
 | 
			
		||||
    list(APPEND nvcc_archs_readable compute_${arch})
 | 
			
		||||
  endforeach()
 | 
			
		||||
 | 
			
		||||
  string(REPLACE ";" " " nvcc_archs_readable "${nvcc_archs_readable}")
 | 
			
		||||
  set(${out_variable}          ${nvcc_flags}          PARENT_SCOPE)
 | 
			
		||||
  set(${out_variable}_readable ${nvcc_archs_readable} PARENT_SCOPE)
 | 
			
		||||
endfunction()
 | 
			
		||||
							
								
								
									
										27
									
								
								docs/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								docs/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
# Minimal makefile for Sphinx documentation
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# You can set these variables from the command line.
 | 
			
		||||
SPHINXOPTS    =
 | 
			
		||||
SPHINXBUILD   = sphinx-build
 | 
			
		||||
SPHINXPROJ    = PyTorch
 | 
			
		||||
SOURCEDIR     = source
 | 
			
		||||
BUILDDIR      = build
 | 
			
		||||
 | 
			
		||||
# Put it first so that "make" without argument is like "make help".
 | 
			
		||||
help:
 | 
			
		||||
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 | 
			
		||||
 | 
			
		||||
docset: html
 | 
			
		||||
	doc2dash --name $(SPHINXPROJ) --icon $(SOURCEDIR)/_static/img/pytorch-logo-flame.png --enable-js --online-redirect-url http://pytorch.org/docs/ --force $(BUILDDIR)/html/
 | 
			
		||||
 | 
			
		||||
	# Manually fix because Zeal doesn't deal well with `icon.png`-only at 2x resolution.
 | 
			
		||||
	cp $(SPHINXPROJ).docset/icon.png $(SPHINXPROJ).docset/icon@2x.png
 | 
			
		||||
	convert $(SPHINXPROJ).docset/icon@2x.png -resize 16x16 $(SPHINXPROJ).docset/icon.png
 | 
			
		||||
 | 
			
		||||
.PHONY: help Makefile docset
 | 
			
		||||
 | 
			
		||||
# Catch-all target: route all unknown targets to Sphinx using the new
 | 
			
		||||
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
 | 
			
		||||
%: Makefile
 | 
			
		||||
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 | 
			
		||||
							
								
								
									
										36
									
								
								docs/make.bat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								docs/make.bat
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
@ECHO OFF
 | 
			
		||||
 | 
			
		||||
pushd %~dp0
 | 
			
		||||
 | 
			
		||||
REM Command file for Sphinx documentation
 | 
			
		||||
 | 
			
		||||
if "%SPHINXBUILD%" == "" (
 | 
			
		||||
	set SPHINXBUILD=sphinx-build
 | 
			
		||||
)
 | 
			
		||||
set SOURCEDIR=source
 | 
			
		||||
set BUILDDIR=build
 | 
			
		||||
set SPHINXPROJ=PyTorch
 | 
			
		||||
 | 
			
		||||
if "%1" == "" goto help
 | 
			
		||||
 | 
			
		||||
%SPHINXBUILD% >NUL 2>NUL
 | 
			
		||||
if errorlevel 9009 (
 | 
			
		||||
	echo.
 | 
			
		||||
	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
 | 
			
		||||
	echo.installed, then set the SPHINXBUILD environment variable to point
 | 
			
		||||
	echo.to the full path of the 'sphinx-build' executable. Alternatively you
 | 
			
		||||
	echo.may add the Sphinx directory to PATH.
 | 
			
		||||
	echo.
 | 
			
		||||
	echo.If you don't have Sphinx installed, grab it from
 | 
			
		||||
	echo.http://sphinx-doc.org/
 | 
			
		||||
	exit /b 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
 | 
			
		||||
goto end
 | 
			
		||||
 | 
			
		||||
:help
 | 
			
		||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
 | 
			
		||||
 | 
			
		||||
:end
 | 
			
		||||
popd
 | 
			
		||||
							
								
								
									
										2
									
								
								docs/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								docs/requirements.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
sphinx
 | 
			
		||||
-e git://github.com/snide/sphinx_rtd_theme.git#egg=sphinx_rtd_theme
 | 
			
		||||
							
								
								
									
										118
									
								
								docs/source/_static/css/pytorch_theme.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								docs/source/_static/css/pytorch_theme.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,118 @@
 | 
			
		||||
body {
 | 
			
		||||
    font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Default header fonts are ugly */
 | 
			
		||||
h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend, p.caption {
 | 
			
		||||
    font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Use white for docs background */
 | 
			
		||||
.wy-side-nav-search {
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-content-wrap, .wy-menu li.current > a  {
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media screen and (min-width: 1400px) {
 | 
			
		||||
    .wy-nav-content-wrap {
 | 
			
		||||
        background-color: rgba(0, 0, 0, 0.0470588);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .wy-nav-content {
 | 
			
		||||
        background-color: #fff;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fixes for mobile */
 | 
			
		||||
.wy-nav-top {
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    background-image: url('../img/pytorch-logo-dark.svg');
 | 
			
		||||
    background-repeat: no-repeat;
 | 
			
		||||
    background-position: center;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    margin: 0.4045em 0.809em;
 | 
			
		||||
    color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-top > a {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media screen and (max-width: 768px) {
 | 
			
		||||
    .wy-side-nav-search>a img.logo {
 | 
			
		||||
        height: 60px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is needed to ensure that logo above search scales properly */
 | 
			
		||||
.wy-side-nav-search a {
 | 
			
		||||
    display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This ensures that multiple constructors will remain in separate lines. */
 | 
			
		||||
.rst-content dl:not(.docutils) dt {
 | 
			
		||||
    display: table;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Use our red for literals (it's very similar to the original color) */
 | 
			
		||||
.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal {
 | 
			
		||||
    color: #F05732;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content tt.xref, a .rst-content tt, .rst-content tt.xref,
 | 
			
		||||
.rst-content code.xref, a .rst-content tt, a .rst-content code {
 | 
			
		||||
    color: #404040;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Change link colors (except for the menu) */
 | 
			
		||||
 | 
			
		||||
a {
 | 
			
		||||
    color: #F05732;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a:hover {
 | 
			
		||||
    color: #F05732;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
a:visited {
 | 
			
		||||
    color: #D44D2C;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu a {
 | 
			
		||||
    color: #b3b3b3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu a:hover {
 | 
			
		||||
    color: #b3b3b3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Default footer text is quite big */
 | 
			
		||||
footer {
 | 
			
		||||
    font-size: 80%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer .rst-footer-buttons {
 | 
			
		||||
    font-size: 125%; /* revert footer settings - 1/80% = 125% */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer p {
 | 
			
		||||
    font-size: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* For hidden headers that appear in TOC tree */
 | 
			
		||||
/* see http://stackoverflow.com/a/32363545/3343043 */
 | 
			
		||||
.rst-content .hidden-section {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav .hidden-section {
 | 
			
		||||
    display: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-nav-search>div.version {
 | 
			
		||||
    color: #000;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								docs/source/_static/img/dynamic_graph.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/source/_static/img/dynamic_graph.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 258 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/source/_static/img/pytorch-logo-dark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/source/_static/img/pytorch-logo-dark.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 27 KiB  | 
							
								
								
									
										24
									
								
								docs/source/_static/img/pytorch-logo-dark.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								docs/source/_static/img/pytorch-logo-dark.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 199.7 40.2" style="enable-background:new 0 0 199.7 40.2;" xml:space="preserve">
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
	.st0{fill:#F05732;}
 | 
			
		||||
	.st1{fill:#9E529F;}
 | 
			
		||||
	.st2{fill:#333333;}
 | 
			
		||||
</style>
 | 
			
		||||
<path class="st0" d="M102.7,12.2c-1.3-1-1.8,3.9-4.4,3.9c-3,0-4-13-6.3-13c-0.7,0-0.8-0.4-7.9,21.3c-2.9,9,4.4,15.8,11.8,15.8
 | 
			
		||||
	c4.6,0,12.3-3,12.3-12.6C108.2,20.5,104.7,13.7,102.7,12.2z M95.8,35.3c-3.7,0-6.7-3.1-6.7-7c0-3.9,3-7,6.7-7s6.7,3.1,6.7,7
 | 
			
		||||
	C102.5,32.1,99.5,35.3,95.8,35.3z"/>
 | 
			
		||||
<path class="st1" d="M99.8,0c-0.5,0-1.8,2.5-1.8,3.6c0,1.5,1,2,1.8,2c0.8,0,1.8-0.5,1.8-2C101.5,2.5,100.2,0,99.8,0z"/>
 | 
			
		||||
<path class="st2" d="M0,39.5V14.9h11.5c5.3,0,8.3,3.6,8.3,7.9c0,4.3-3,7.9-8.3,7.9H5.2v8.8H0z M14.4,22.8c0-2.1-1.6-3.3-3.7-3.3H5.2
 | 
			
		||||
	v6.6h5.5C12.8,26.1,14.4,24.8,14.4,22.8z"/>
 | 
			
		||||
<path class="st2" d="M35.2,39.5V29.4l-9.4-14.5h6l6.1,9.8l6.1-9.8h5.9l-9.4,14.5v10.1H35.2z"/>
 | 
			
		||||
<path class="st2" d="M63.3,39.5v-20h-7.2v-4.6h19.6v4.6h-7.2v20H63.3z"/>
 | 
			
		||||
<path class="st2" d="M131.4,39.5l-4.8-8.7h-3.8v8.7h-5.2V14.9H129c5.1,0,8.3,3.4,8.3,7.9c0,4.3-2.8,6.7-5.4,7.3l5.6,9.4H131.4z
 | 
			
		||||
	 M131.9,22.8c0-2-1.6-3.3-3.7-3.3h-5.5v6.6h5.5C130.3,26.1,131.9,24.9,131.9,22.8z"/>
 | 
			
		||||
<path class="st2" d="M145.6,27.2c0-7.6,5.7-12.7,13.1-12.7c5.4,0,8.5,2.9,10.3,6l-4.5,2.2c-1-2-3.2-3.6-5.8-3.6
 | 
			
		||||
	c-4.5,0-7.7,3.4-7.7,8.1c0,4.6,3.2,8.1,7.7,8.1c2.5,0,4.7-1.6,5.8-3.6l4.5,2.2c-1.7,3.1-4.9,6-10.3,6
 | 
			
		||||
	C151.3,39.9,145.6,34.7,145.6,27.2z"/>
 | 
			
		||||
<path class="st2" d="M194.5,39.5V29.1h-11.6v10.4h-5.2V14.9h5.2v9.7h11.6v-9.7h5.3v24.6H194.5z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/source/_static/img/pytorch-logo-flame.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/source/_static/img/pytorch-logo-flame.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1010 B  | 
							
								
								
									
										33
									
								
								docs/source/_static/img/pytorch-logo-flame.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								docs/source/_static/img/pytorch-logo-flame.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   height="40.200001"
 | 
			
		||||
   width="40.200001"
 | 
			
		||||
   xml:space="preserve"
 | 
			
		||||
   viewBox="0 0 40.200002 40.2"
 | 
			
		||||
   y="0px"
 | 
			
		||||
   x="0px"
 | 
			
		||||
   id="Layer_1"
 | 
			
		||||
   version="1.1"><metadata
 | 
			
		||||
     id="metadata4717"><rdf:RDF><cc:Work
 | 
			
		||||
         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
 | 
			
		||||
     id="defs4715" /><style
 | 
			
		||||
     id="style4694"
 | 
			
		||||
     type="text/css">
 | 
			
		||||
	.st0{fill:#F05732;}
 | 
			
		||||
	.st1{fill:#9E529F;}
 | 
			
		||||
	.st2{fill:#333333;}
 | 
			
		||||
</style><path
 | 
			
		||||
     style="fill:#f05732"
 | 
			
		||||
     id="path4696"
 | 
			
		||||
     d="m 26.975479,12.199999 c -1.3,-1 -1.8,3.9 -4.4,3.9 -3,0 -4,-12.9999998 -6.3,-12.9999998 -0.7,0 -0.8,-0.4 -7.9000003,21.2999998 -2.9000001,9 4.4000003,15.8 11.8000003,15.8 4.6,0 12.3,-3 12.3,-12.6 0,-7.1 -3.5,-13.9 -5.5,-15.4 z m -6.9,23.1 c -3.7,0 -6.7,-3.1 -6.7,-7 0,-3.9 3,-7 6.7,-7 3.7,0 6.7,3.1 6.7,7 0,3.8 -3,7 -6.7,7 z"
 | 
			
		||||
     class="st0" /><path
 | 
			
		||||
     style="fill:#9e529f"
 | 
			
		||||
     id="path4698"
 | 
			
		||||
     d="m 24.075479,-7.6293945e-7 c -0.5,0 -1.8,2.49999996293945 -1.8,3.59999996293945 0,1.5 1,2 1.8,2 0.8,0 1.8,-0.5 1.8,-2 -0.1,-1.1 -1.4,-3.59999996293945 -1.8,-3.59999996293945 z"
 | 
			
		||||
     class="st1" /></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/source/_static/img/tensor_illustration.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/source/_static/img/tensor_illustration.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										55
									
								
								docs/source/autograd.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								docs/source/autograd.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
.. role:: hidden
 | 
			
		||||
    :class: hidden-section
 | 
			
		||||
 | 
			
		||||
Automatic differentiation package - torch.autograd
 | 
			
		||||
==================================================
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.autograd
 | 
			
		||||
.. currentmodule:: torch.autograd
 | 
			
		||||
 | 
			
		||||
.. autofunction:: backward
 | 
			
		||||
 | 
			
		||||
.. autofunction:: grad
 | 
			
		||||
 | 
			
		||||
Variable
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
API compatibility
 | 
			
		||||
^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Variable API is nearly the same as regular Tensor API (with the exception
 | 
			
		||||
of a couple in-place methods, that would overwrite inputs required for
 | 
			
		||||
gradient computation). In most cases Tensors can be safely replaced with
 | 
			
		||||
Variables and the code will remain to work just fine. Because of this,
 | 
			
		||||
we're not documenting all the operations on variables, and you should
 | 
			
		||||
refer to :class:`torch.Tensor` docs for this purpose.
 | 
			
		||||
 | 
			
		||||
In-place operations on Variables
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Supporting in-place operations in autograd is a hard matter, and we discourage
 | 
			
		||||
their use in most cases. Autograd's aggressive buffer freeing and reuse makes
 | 
			
		||||
it very efficient and there are very few occasions when in-place operations
 | 
			
		||||
actually lower memory usage by any significant amount. Unless you're operating
 | 
			
		||||
under heavy memory pressure, you might never need to use them.
 | 
			
		||||
 | 
			
		||||
In-place correctness checks
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
All :class:`Variable` s keep track of in-place operations applied to them, and
 | 
			
		||||
if the implementation detects that a variable was saved for backward in one of
 | 
			
		||||
the functions, but it was modified in-place afterwards, an error will be raised
 | 
			
		||||
once backward pass is started. This ensures that if you're using in-place
 | 
			
		||||
functions and not seeing any errors, you can be sure that the computed
 | 
			
		||||
gradients are correct.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Variable
 | 
			
		||||
    :members:
 | 
			
		||||
 | 
			
		||||
:hidden:`Function`
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Function
 | 
			
		||||
    :members:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										249
									
								
								docs/source/conf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								docs/source/conf.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,249 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# PyTorch documentation build configuration file, created by
 | 
			
		||||
# sphinx-quickstart on Fri Dec 23 13:31:47 2016.
 | 
			
		||||
#
 | 
			
		||||
# This file is execfile()d with the current directory set to its
 | 
			
		||||
# containing dir.
 | 
			
		||||
#
 | 
			
		||||
# Note that not all possible configuration values are present in this
 | 
			
		||||
# autogenerated file.
 | 
			
		||||
#
 | 
			
		||||
# All configuration values have a default; values that are commented out
 | 
			
		||||
# serve to show the default.
 | 
			
		||||
 | 
			
		||||
# If extensions (or modules to document with autodoc) are in another directory,
 | 
			
		||||
# add these directories to sys.path here. If the directory is relative to the
 | 
			
		||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
 | 
			
		||||
#
 | 
			
		||||
# import os
 | 
			
		||||
# import sys
 | 
			
		||||
# sys.path.insert(0, os.path.abspath('.'))
 | 
			
		||||
import torch
 | 
			
		||||
try:
 | 
			
		||||
    import torchvision
 | 
			
		||||
except ImportError:
 | 
			
		||||
    import warnings
 | 
			
		||||
    warnings.warn('unable to load "torchvision" package')
 | 
			
		||||
import sphinx_rtd_theme
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- General configuration ------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# If your documentation needs a minimal Sphinx version, state it here.
 | 
			
		||||
#
 | 
			
		||||
# needs_sphinx = '1.0'
 | 
			
		||||
 | 
			
		||||
# Add any Sphinx extension module names here, as strings. They can be
 | 
			
		||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 | 
			
		||||
# ones.
 | 
			
		||||
extensions = [
 | 
			
		||||
    'sphinx.ext.autodoc',
 | 
			
		||||
    'sphinx.ext.autosummary',
 | 
			
		||||
    'sphinx.ext.doctest',
 | 
			
		||||
    'sphinx.ext.intersphinx',
 | 
			
		||||
    'sphinx.ext.todo',
 | 
			
		||||
    'sphinx.ext.coverage',
 | 
			
		||||
    'sphinx.ext.mathjax',
 | 
			
		||||
    'sphinx.ext.napoleon',
 | 
			
		||||
    'sphinx.ext.viewcode',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
napoleon_use_ivar = True
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain templates here, relative to this directory.
 | 
			
		||||
templates_path = ['_templates']
 | 
			
		||||
 | 
			
		||||
# The suffix(es) of source filenames.
 | 
			
		||||
# You can specify multiple suffix as a list of string:
 | 
			
		||||
#
 | 
			
		||||
# source_suffix = ['.rst', '.md']
 | 
			
		||||
source_suffix = '.rst'
 | 
			
		||||
 | 
			
		||||
# The master toctree document.
 | 
			
		||||
master_doc = 'index'
 | 
			
		||||
 | 
			
		||||
# General information about the project.
 | 
			
		||||
project = 'PyTorch'
 | 
			
		||||
copyright = '2017, Torch Contributors'
 | 
			
		||||
author = 'Torch Contributors'
 | 
			
		||||
 | 
			
		||||
# The version info for the project you're documenting, acts as replacement for
 | 
			
		||||
# |version| and |release|, also used in various other places throughout the
 | 
			
		||||
# built documents.
 | 
			
		||||
#
 | 
			
		||||
# The short X.Y version.
 | 
			
		||||
# TODO: change to [:2] at v1.0
 | 
			
		||||
version = 'master (' + torch.__version__ + ' )'
 | 
			
		||||
# The full version, including alpha/beta/rc tags.
 | 
			
		||||
# TODO: verify this works as expected
 | 
			
		||||
release = 'master'
 | 
			
		||||
 | 
			
		||||
# The language for content autogenerated by Sphinx. Refer to documentation
 | 
			
		||||
# for a list of supported languages.
 | 
			
		||||
#
 | 
			
		||||
# This is also used if you do content translation via gettext catalogs.
 | 
			
		||||
# Usually you set "language" from the command line for these cases.
 | 
			
		||||
language = None
 | 
			
		||||
 | 
			
		||||
# List of patterns, relative to source directory, that match files and
 | 
			
		||||
# directories to ignore when looking for source files.
 | 
			
		||||
# This patterns also effect to html_static_path and html_extra_path
 | 
			
		||||
exclude_patterns = []
 | 
			
		||||
 | 
			
		||||
# The name of the Pygments (syntax highlighting) style to use.
 | 
			
		||||
pygments_style = 'sphinx'
 | 
			
		||||
 | 
			
		||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
 | 
			
		||||
todo_include_todos = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for HTML output ----------------------------------------------
 | 
			
		||||
 | 
			
		||||
# The theme to use for HTML and HTML Help pages.  See the documentation for
 | 
			
		||||
# a list of builtin themes.
 | 
			
		||||
#
 | 
			
		||||
html_theme = 'sphinx_rtd_theme'
 | 
			
		||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
 | 
			
		||||
 | 
			
		||||
# Theme options are theme-specific and customize the look and feel of a theme
 | 
			
		||||
# further.  For a list of options available for each theme, see the
 | 
			
		||||
# documentation.
 | 
			
		||||
#
 | 
			
		||||
html_theme_options = {
 | 
			
		||||
    'collapse_navigation': False,
 | 
			
		||||
    'display_version': True,
 | 
			
		||||
    'logo_only': True,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html_logo = '_static/img/pytorch-logo-dark.svg'
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain custom static files (such as style sheets) here,
 | 
			
		||||
# relative to this directory. They are copied after the builtin static files,
 | 
			
		||||
# so a file named "default.css" will overwrite the builtin "default.css".
 | 
			
		||||
html_static_path = ['_static']
 | 
			
		||||
 | 
			
		||||
# html_style_path = 'css/pytorch_theme.css'
 | 
			
		||||
html_context = {
 | 
			
		||||
    'css_files': [
 | 
			
		||||
        'https://fonts.googleapis.com/css?family=Lato',
 | 
			
		||||
        '_static/css/pytorch_theme.css'
 | 
			
		||||
    ],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for HTMLHelp output ------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Output file base name for HTML help builder.
 | 
			
		||||
htmlhelp_basename = 'PyTorchdoc'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for LaTeX output ---------------------------------------------
 | 
			
		||||
 | 
			
		||||
latex_elements = {
 | 
			
		||||
    # The paper size ('letterpaper' or 'a4paper').
 | 
			
		||||
    #
 | 
			
		||||
    # 'papersize': 'letterpaper',
 | 
			
		||||
 | 
			
		||||
    # The font size ('10pt', '11pt' or '12pt').
 | 
			
		||||
    #
 | 
			
		||||
    # 'pointsize': '10pt',
 | 
			
		||||
 | 
			
		||||
    # Additional stuff for the LaTeX preamble.
 | 
			
		||||
    #
 | 
			
		||||
    # 'preamble': '',
 | 
			
		||||
 | 
			
		||||
    # Latex figure (float) alignment
 | 
			
		||||
    #
 | 
			
		||||
    # 'figure_align': 'htbp',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into LaTeX files. List of tuples
 | 
			
		||||
# (source start file, target name, title,
 | 
			
		||||
#  author, documentclass [howto, manual, or own class]).
 | 
			
		||||
latex_documents = [
 | 
			
		||||
    (master_doc, 'pytorch.tex', 'PyTorch Documentation',
 | 
			
		||||
     'Torch Contributors', 'manual'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for manual page output ---------------------------------------
 | 
			
		||||
 | 
			
		||||
# One entry per manual page. List of tuples
 | 
			
		||||
# (source start file, name, description, authors, manual section).
 | 
			
		||||
man_pages = [
 | 
			
		||||
    (master_doc, 'PyTorch', 'PyTorch Documentation',
 | 
			
		||||
     [author], 1)
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for Texinfo output -------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into Texinfo files. List of tuples
 | 
			
		||||
# (source start file, target name, title, author,
 | 
			
		||||
#  dir menu entry, description, category)
 | 
			
		||||
texinfo_documents = [
 | 
			
		||||
    (master_doc, 'PyTorch', 'PyTorch Documentation',
 | 
			
		||||
     author, 'PyTorch', 'One line description of project.',
 | 
			
		||||
     'Miscellaneous'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Example configuration for intersphinx: refer to the Python standard library.
 | 
			
		||||
intersphinx_mapping = {
 | 
			
		||||
    'python': ('https://docs.python.org/', None),
 | 
			
		||||
    'numpy': ('http://docs.scipy.org/doc/numpy/', None),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# -- A patch that prevents Sphinx from cross-referencing ivar tags -------
 | 
			
		||||
# See http://stackoverflow.com/a/41184353/3343043
 | 
			
		||||
 | 
			
		||||
from docutils import nodes
 | 
			
		||||
from sphinx.util.docfields import TypedField
 | 
			
		||||
from sphinx import addnodes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def patched_make_field(self, types, domain, items, **kw):
 | 
			
		||||
    # `kw` catches `env=None` needed for newer sphinx while maintaining
 | 
			
		||||
    #  backwards compatibility when passed along further down!
 | 
			
		||||
 | 
			
		||||
    # type: (List, unicode, Tuple) -> nodes.field
 | 
			
		||||
    def handle_item(fieldarg, content):
 | 
			
		||||
        par = nodes.paragraph()
 | 
			
		||||
        par += addnodes.literal_strong('', fieldarg)  # Patch: this line added
 | 
			
		||||
        # par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
 | 
			
		||||
        #                           addnodes.literal_strong))
 | 
			
		||||
        if fieldarg in types:
 | 
			
		||||
            par += nodes.Text(' (')
 | 
			
		||||
            # NOTE: using .pop() here to prevent a single type node to be
 | 
			
		||||
            # inserted twice into the doctree, which leads to
 | 
			
		||||
            # inconsistencies later when references are resolved
 | 
			
		||||
            fieldtype = types.pop(fieldarg)
 | 
			
		||||
            if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
 | 
			
		||||
                typename = u''.join(n.astext() for n in fieldtype)
 | 
			
		||||
                typename = typename.replace('int', 'python:int')
 | 
			
		||||
                typename = typename.replace('long', 'python:long')
 | 
			
		||||
                typename = typename.replace('float', 'python:float')
 | 
			
		||||
                typename = typename.replace('type', 'python:type')
 | 
			
		||||
                par.extend(self.make_xrefs(self.typerolename, domain, typename,
 | 
			
		||||
                                           addnodes.literal_emphasis, **kw))
 | 
			
		||||
            else:
 | 
			
		||||
                par += fieldtype
 | 
			
		||||
            par += nodes.Text(')')
 | 
			
		||||
        par += nodes.Text(' -- ')
 | 
			
		||||
        par += content
 | 
			
		||||
        return par
 | 
			
		||||
 | 
			
		||||
    fieldname = nodes.field_name('', self.label)
 | 
			
		||||
    if len(items) == 1 and self.can_collapse:
 | 
			
		||||
        fieldarg, content = items[0]
 | 
			
		||||
        bodynode = handle_item(fieldarg, content)
 | 
			
		||||
    else:
 | 
			
		||||
        bodynode = self.list_type()
 | 
			
		||||
        for fieldarg, content in items:
 | 
			
		||||
            bodynode += nodes.list_item('', handle_item(fieldarg, content))
 | 
			
		||||
    fieldbody = nodes.field_body('', bodynode)
 | 
			
		||||
    return nodes.field('', fieldname, fieldbody)
 | 
			
		||||
 | 
			
		||||
TypedField.make_field = patched_make_field
 | 
			
		||||
							
								
								
									
										34
									
								
								docs/source/cuda.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								docs/source/cuda.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
torch.cuda
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torch.cuda
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.cuda
 | 
			
		||||
   :members:
 | 
			
		||||
 | 
			
		||||
Communication collectives
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
.. autofunction:: torch.cuda.comm.broadcast
 | 
			
		||||
 | 
			
		||||
.. autofunction:: torch.cuda.comm.reduce_add
 | 
			
		||||
 | 
			
		||||
.. autofunction:: torch.cuda.comm.scatter
 | 
			
		||||
 | 
			
		||||
.. autofunction:: torch.cuda.comm.gather
 | 
			
		||||
 | 
			
		||||
Streams and events
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Stream
 | 
			
		||||
   :members:
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Event
 | 
			
		||||
   :members:
 | 
			
		||||
 | 
			
		||||
NVIDIA Tools Extension (NVTX)
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
.. autofunction:: torch.cuda.nvtx.mark
 | 
			
		||||
.. autofunction:: torch.cuda.nvtx.range_push
 | 
			
		||||
.. autofunction:: torch.cuda.nvtx.range_pop
 | 
			
		||||
							
								
								
									
										13
									
								
								docs/source/data.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								docs/source/data.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
torch.utils.data
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.utils.data
 | 
			
		||||
.. autoclass:: Dataset
 | 
			
		||||
.. autoclass:: TensorDataset
 | 
			
		||||
.. autoclass:: DataLoader
 | 
			
		||||
.. autoclass:: torch.utils.data.sampler.Sampler
 | 
			
		||||
.. autoclass:: torch.utils.data.sampler.SequentialSampler
 | 
			
		||||
.. autoclass:: torch.utils.data.sampler.RandomSampler
 | 
			
		||||
.. autoclass:: torch.utils.data.sampler.SubsetRandomSampler
 | 
			
		||||
.. autoclass:: torch.utils.data.sampler.WeightedRandomSampler
 | 
			
		||||
.. autoclass:: torch.utils.data.distributed.DistributedSampler
 | 
			
		||||
							
								
								
									
										165
									
								
								docs/source/distributed.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								docs/source/distributed.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,165 @@
 | 
			
		||||
.. role:: hidden
 | 
			
		||||
    :class: hidden-section
 | 
			
		||||
 | 
			
		||||
Distributed communication package - torch.distributed
 | 
			
		||||
=====================================================
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.distributed
 | 
			
		||||
.. currentmodule:: torch.distributed
 | 
			
		||||
 | 
			
		||||
Currently torch.distributed supports three backends, each with
 | 
			
		||||
different capabilities. The table below shows which functions are available
 | 
			
		||||
for use with CPU / CUDA tensors.
 | 
			
		||||
MPI supports cuda only iff the implementation used to build PyTorch supports it.
 | 
			
		||||
 | 
			
		||||
+------------+-----------+-----------+-----------+
 | 
			
		||||
| Backend    | ``tcp``   | ``gloo``  | ``mpi``   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| Device     | CPU | GPU | CPU | GPU | CPU | GPU |
 | 
			
		||||
+============+=====+=====+=====+=====+=====+=====+
 | 
			
		||||
| send       | ✓   | ✘   | ✘   | ✘   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| recv       | ✓   | ✘   | ✘   | ✘   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| broadcast  | ✓   | ✘   | ✓   | ✓   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| all_reduce | ✓   | ✘   | ✓   | ✓   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| reduce     | ✓   | ✘   | ✘   | ✘   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| all_gather | ✓   | ✘   | ✘   | ✘   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| gather     | ✓   | ✘   | ✘   | ✘   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| scatter    | ✓   | ✘   | ✘   | ✘   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
| barrier    | ✓   | ✘   | ✓   | ✓   | ✓   | ?   |
 | 
			
		||||
+------------+-----+-----+-----+-----+-----+-----+
 | 
			
		||||
 | 
			
		||||
Initialization
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
The package needs to be initialized using the :func:`torch.distributed.init_process_group`
 | 
			
		||||
function before calling any other methods.
 | 
			
		||||
 | 
			
		||||
.. autofunction:: init_process_group
 | 
			
		||||
 | 
			
		||||
.. autofunction:: get_rank
 | 
			
		||||
 | 
			
		||||
.. autofunction:: get_world_size
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Currently three initialization methods are supported:
 | 
			
		||||
 | 
			
		||||
TCP initialization
 | 
			
		||||
^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Initialization will utilize a network address reachable from all processes.
 | 
			
		||||
If the address belongs to one of the machines, initialization requires that all processes
 | 
			
		||||
have manually specified ranks.
 | 
			
		||||
 | 
			
		||||
Alternatively, the address has to be a valid IP multicast address, in which case,
 | 
			
		||||
ranks can be assigned automatically. Multicast initialization also supports
 | 
			
		||||
a ``group_name`` argument, which allows you to use the same address for multiple jobs,
 | 
			
		||||
as long as they use different group names.
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
    import torch.distributed as dist
 | 
			
		||||
 | 
			
		||||
    # Use address of one of the machines
 | 
			
		||||
    dist.init_process_group(init_method='tcp://10.1.1.20:23456', rank=args.rank, world_size=4)
 | 
			
		||||
 | 
			
		||||
    # or a multicast address - rank will be assigned automatically if unspecified
 | 
			
		||||
    dist.init_process_group(init_method='tcp://[ff15:1e18:5d4c:4cf0:d02d:b659:53ba:b0a7]:23456',
 | 
			
		||||
                            world_size=4)
 | 
			
		||||
 | 
			
		||||
Shared file-system initialization
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Another initialization method makes use of a file system shared and visible from
 | 
			
		||||
all machines in a group. The URL should start with ``file://`` and contain a path
 | 
			
		||||
to a non-existent file (in an existing directory) on a shared file system.
 | 
			
		||||
This initialization method also supports a ``group_name`` argument, which allows you to
 | 
			
		||||
use the same shared file path for multiple jobs, as long as they use different
 | 
			
		||||
group names.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
    This method assumes that the file system supports locking using ``fcntl`` - most
 | 
			
		||||
    local systems and NFS support it.
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
    import torch.distributed as dist
 | 
			
		||||
 | 
			
		||||
    # Rank will be assigned automatically if unspecified
 | 
			
		||||
    dist.init_process_group(init_method='file:///mnt/nfs/sharedfile', world_size=4,
 | 
			
		||||
                            group_name=args.group)
 | 
			
		||||
 | 
			
		||||
Environment variable initialization
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
This method will read the configuration from environment variables, allowing
 | 
			
		||||
one to fully customize how the information is obtained. The variables to be set
 | 
			
		||||
are:
 | 
			
		||||
 | 
			
		||||
* ``MASTER_PORT`` - required; has to be a free port on machine with rank 0
 | 
			
		||||
* ``MASTER_ADDR`` - required (except for rank 0); address of rank 0 node
 | 
			
		||||
* ``WORLD_SIZE`` - required; can be set either here, or in a call to init function
 | 
			
		||||
* ``RANK`` - required; can be set either here, or in a call to init function
 | 
			
		||||
 | 
			
		||||
The machine with rank 0 will be used to set up all connections.
 | 
			
		||||
 | 
			
		||||
This is the default method, meaning that ``init_method`` does not have to be specified (or
 | 
			
		||||
can be ``env://``).
 | 
			
		||||
 | 
			
		||||
Groups
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
By default collectives operate on the default group (also called the world) and
 | 
			
		||||
require all processes to enter the distributed function call. However, some workloads can benefit
 | 
			
		||||
from more fine-grained communication. This is where distributed groups come
 | 
			
		||||
into play. :func:`~torch.distributed.new_group` function can be
 | 
			
		||||
used to create new groups, with arbitrary subsets of all processes. It returns
 | 
			
		||||
an opaque group handle that can be given as a ``group`` argument to all collectives
 | 
			
		||||
(collectives are distributed functions to exchange information in certain well-known programming patterns).
 | 
			
		||||
 | 
			
		||||
.. autofunction:: new_group
 | 
			
		||||
 | 
			
		||||
Point-to-point communication
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
.. autofunction:: send
 | 
			
		||||
 | 
			
		||||
.. autofunction:: recv
 | 
			
		||||
 | 
			
		||||
:func:`~torch.distributed.isend` and :func:`~torch.distributed.irecv`
 | 
			
		||||
return distributed request objects when used. In general, the type of this object is unspecified
 | 
			
		||||
as they should never be created manually, but they are guaranteed to support two methods:
 | 
			
		||||
 | 
			
		||||
* ``is_completed()`` - returns True if the operation has finished
 | 
			
		||||
* ``wait()`` - will block the process until the operation is finished.
 | 
			
		||||
  ``is_completed()`` is guaranteed to return True once it returns.
 | 
			
		||||
 | 
			
		||||
.. autofunction:: isend
 | 
			
		||||
 | 
			
		||||
.. autofunction:: irecv
 | 
			
		||||
 | 
			
		||||
Collective functions
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
.. autofunction:: broadcast
 | 
			
		||||
 | 
			
		||||
.. autofunction:: all_reduce
 | 
			
		||||
 | 
			
		||||
.. autofunction:: reduce
 | 
			
		||||
 | 
			
		||||
.. autofunction:: all_gather
 | 
			
		||||
 | 
			
		||||
.. autofunction:: gather
 | 
			
		||||
 | 
			
		||||
.. autofunction:: scatter
 | 
			
		||||
 | 
			
		||||
.. autofunction:: barrier
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								docs/source/ffi.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								docs/source/ffi.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
torch.utils.ffi
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torch.utils.ffi
 | 
			
		||||
.. autofunction:: create_extension
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								docs/source/index.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								docs/source/index.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
.. PyTorch documentation master file, created by
 | 
			
		||||
   sphinx-quickstart on Fri Dec 23 13:31:47 2016.
 | 
			
		||||
   You can adapt this file completely to your liking, but it should at least
 | 
			
		||||
   contain the root `toctree` directive.
 | 
			
		||||
 | 
			
		||||
:github_url: https://github.com/pytorch/pytorch
 | 
			
		||||
 | 
			
		||||
PyTorch documentation
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
PyTorch is an optimized tensor library for deep learning using GPUs and CPUs.
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :glob:
 | 
			
		||||
   :maxdepth: 1
 | 
			
		||||
   :caption: Notes
 | 
			
		||||
 | 
			
		||||
   notes/*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 1
 | 
			
		||||
   :caption: Package Reference
 | 
			
		||||
 | 
			
		||||
   torch
 | 
			
		||||
   tensors
 | 
			
		||||
   sparse
 | 
			
		||||
   storage
 | 
			
		||||
   nn
 | 
			
		||||
   optim
 | 
			
		||||
   torch.autograd <autograd>
 | 
			
		||||
   torch.multiprocessing <multiprocessing>
 | 
			
		||||
   torch.distributed <distributed>
 | 
			
		||||
   torch.legacy <legacy>
 | 
			
		||||
   cuda
 | 
			
		||||
   ffi
 | 
			
		||||
   data
 | 
			
		||||
   model_zoo
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :glob:
 | 
			
		||||
   :maxdepth: 1
 | 
			
		||||
   :caption: torchvision Reference
 | 
			
		||||
 | 
			
		||||
   torchvision/torchvision
 | 
			
		||||
   torchvision/datasets
 | 
			
		||||
   torchvision/models
 | 
			
		||||
   torchvision/transforms
 | 
			
		||||
   torchvision/utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Indices and tables
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
* :ref:`genindex`
 | 
			
		||||
* :ref:`modindex`
 | 
			
		||||
							
								
								
									
										4
									
								
								docs/source/legacy.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docs/source/legacy.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
Legacy package - torch.legacy
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.legacy
 | 
			
		||||
							
								
								
									
										5
									
								
								docs/source/model_zoo.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								docs/source/model_zoo.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
torch.utils.model_zoo
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.utils.model_zoo
 | 
			
		||||
.. autofunction:: load_url
 | 
			
		||||
							
								
								
									
										88
									
								
								docs/source/multiprocessing.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								docs/source/multiprocessing.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
			
		||||
Multiprocessing package - torch.multiprocessing
 | 
			
		||||
===============================================
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.multiprocessing
 | 
			
		||||
.. currentmodule:: torch.multiprocessing
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    If the main process exits abruptly (e.g. because of an incoming signal),
 | 
			
		||||
    Python's ``multiprocessing`` sometimes fails to clean up its children.
 | 
			
		||||
    It's a known caveat, so if you're seeing any resource leaks after
 | 
			
		||||
    interrupting the interpreter, it probably means that this has just happened
 | 
			
		||||
    to you.
 | 
			
		||||
 | 
			
		||||
Strategy management
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
.. autofunction:: get_all_sharing_strategies
 | 
			
		||||
.. autofunction:: get_sharing_strategy
 | 
			
		||||
.. autofunction:: set_sharing_strategy
 | 
			
		||||
 | 
			
		||||
Sharing CUDA tensors
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
Sharing CUDA tensors between processes is supported only in Python 3, using
 | 
			
		||||
a ``spawn`` or ``forkserver`` start methods. :mod:`python:multiprocessing` in
 | 
			
		||||
Python 2 can only create subprocesses using ``fork``, and it's not supported
 | 
			
		||||
by the CUDA runtime.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    CUDA API requires that the allocation exported to other processes remains
 | 
			
		||||
    valid as long as it's used by them. You should be careful and ensure that
 | 
			
		||||
    CUDA tensors you shared don't go out of scope as long as it's necessary.
 | 
			
		||||
    This shouldn't be a problem for sharing model parameters, but passing other
 | 
			
		||||
    kinds of data should be done with care. Note that this restriction doesn't
 | 
			
		||||
    apply to shared CPU memory.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Sharing strategies
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
This section provides a brief overview into how different sharing strategies
 | 
			
		||||
work. Note that it applies only to CPU tensor - CUDA tensors will always use
 | 
			
		||||
the CUDA API, as that's the only way they can be shared.
 | 
			
		||||
 | 
			
		||||
File descriptor - ``file_descriptor``
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    This is the default strategy (except for macOS and OS X where it's not
 | 
			
		||||
    supported).
 | 
			
		||||
 | 
			
		||||
This strategy will use file descriptors as shared memory handles. Whenever a
 | 
			
		||||
storage is moved to shared memory, a file descriptor obtained from ``shm_open``
 | 
			
		||||
is cached with the object, and when it's going to be sent to other processes,
 | 
			
		||||
the file descriptor will be transferred (e.g. via UNIX sockets) to it. The
 | 
			
		||||
receiver will also cache the file descriptor and ``mmap`` it, to obtain a shared
 | 
			
		||||
view onto the storage data.
 | 
			
		||||
 | 
			
		||||
Note that if there will be a lot of tensors shared, this strategy will keep a
 | 
			
		||||
large number of file descriptors open most of the time. If your system has low
 | 
			
		||||
limits for the number of open file descriptors, and you can't rise them, you
 | 
			
		||||
should use the ``file_system`` strategy.
 | 
			
		||||
 | 
			
		||||
File system - ``file_system``
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
This strategy will use file names given to ``shm_open`` to identify the shared
 | 
			
		||||
memory regions. This has a benefit of not requiring the implementation to cache
 | 
			
		||||
the file descriptors obtained from it, but at the same time is prone to shared
 | 
			
		||||
memory leaks. The file can't be deleted right after its creation, because other
 | 
			
		||||
processes need to access it to open their views. If the processes fatally
 | 
			
		||||
crash, or are killed, and don't call the storage destructors, the files will
 | 
			
		||||
remain in the system. This is very serious, because they keep using up the
 | 
			
		||||
memory until the system is restarted, or they're freed manually.
 | 
			
		||||
 | 
			
		||||
To counter the problem of shared memory file leaks, :mod:`torch.multiprocessing`
 | 
			
		||||
will spawn a daemon named ``torch_shm_manager`` that will isolate itself from
 | 
			
		||||
the current process group, and will keep track of all shared memory allocations.
 | 
			
		||||
Once all processes connected to it exit, it will wait a moment to ensure there
 | 
			
		||||
will be no new connections, and will iterate over all shared memory files
 | 
			
		||||
allocated by the group. If it finds that any of them still exist, they will be
 | 
			
		||||
deallocated. We've tested this method and it proved to be robust to various
 | 
			
		||||
failures. Still, if your system has high enough limits, and ``file_descriptor``
 | 
			
		||||
is a supported strategy, we do not recommend switching to this one.
 | 
			
		||||
							
								
								
									
										1082
									
								
								docs/source/nn.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1082
									
								
								docs/source/nn.rst
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										151
									
								
								docs/source/notes/autograd.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								docs/source/notes/autograd.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,151 @@
 | 
			
		||||
Autograd mechanics
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
This note will present an overview of how autograd works and records the
 | 
			
		||||
operations. It's not strictly necessary to understand all this, but we recommend
 | 
			
		||||
getting familiar with it, as it will help you write more efficient, cleaner
 | 
			
		||||
programs, and can aid you in debugging.
 | 
			
		||||
 | 
			
		||||
.. _excluding-subgraphs:
 | 
			
		||||
 | 
			
		||||
Excluding subgraphs from backward
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Every Variable has two flags: :attr:`requires_grad` and :attr:`volatile`.
 | 
			
		||||
They both allow for fine grained exclusion of subgraphs from gradient
 | 
			
		||||
computation and can increase efficiency.
 | 
			
		||||
 | 
			
		||||
.. _excluding-requires_grad:
 | 
			
		||||
 | 
			
		||||
``requires_grad``
 | 
			
		||||
~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
If there's a single input to an operation that requires gradient, its output
 | 
			
		||||
will also require gradient. Conversely, only if all inputs don't require
 | 
			
		||||
gradient, the output also won't require it. Backward computation is never
 | 
			
		||||
performed in the subgraphs, where all Variables didn't require gradients.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    >>> x = Variable(torch.randn(5, 5))
 | 
			
		||||
    >>> y = Variable(torch.randn(5, 5))
 | 
			
		||||
    >>> z = Variable(torch.randn(5, 5), requires_grad=True)
 | 
			
		||||
    >>> a = x + y
 | 
			
		||||
    >>> a.requires_grad
 | 
			
		||||
    False
 | 
			
		||||
    >>> b = a + z
 | 
			
		||||
    >>> b.requires_grad
 | 
			
		||||
    True
 | 
			
		||||
 | 
			
		||||
This is especially useful when you want to freeze part of your model, or you
 | 
			
		||||
know in advance that you're not going to use gradients w.r.t. some parameters.
 | 
			
		||||
For example if you want to finetune a pretrained CNN, it's enough to switch the
 | 
			
		||||
:attr:`requires_grad` flags in the frozen base, and no intermediate buffers will
 | 
			
		||||
be saved, until the computation gets to the last layer, where the affine
 | 
			
		||||
transform will use weights that require gradient, and the output of the network
 | 
			
		||||
will also require them.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    model = torchvision.models.resnet18(pretrained=True)
 | 
			
		||||
    for param in model.parameters():
 | 
			
		||||
        param.requires_grad = False
 | 
			
		||||
    # Replace the last fully-connected layer
 | 
			
		||||
    # Parameters of newly constructed modules have requires_grad=True by default
 | 
			
		||||
    model.fc = nn.Linear(512, 100)
 | 
			
		||||
 | 
			
		||||
    # Optimize only the classifier
 | 
			
		||||
    optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)
 | 
			
		||||
 | 
			
		||||
``volatile``
 | 
			
		||||
~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
Volatile is recommended for purely inference mode, when you're sure you won't
 | 
			
		||||
be even calling `.backward()`. It's more efficient than any other autograd
 | 
			
		||||
setting - it will use the absolute minimal amount of memory to evaluate the
 | 
			
		||||
model. ``volatile`` also determines that ``requires_grad is False``.
 | 
			
		||||
 | 
			
		||||
Volatile differs from :ref:`excluding-requires_grad` in how the flag propagates.
 | 
			
		||||
If there's even a single volatile input to an operation, its output is also
 | 
			
		||||
going to be volatile. Volatility spreads across the graph much easier than
 | 
			
		||||
non-requiring gradient - you only need a **single** volatile leaf to have a
 | 
			
		||||
volatile output, while you need **all** leaves to not require gradient to
 | 
			
		||||
have an output that doesn't require gradient. Using volatile flag you don't
 | 
			
		||||
need to change any settings of your model parameters to use it for
 | 
			
		||||
inference. It's enough to create a volatile input, and this will ensure that
 | 
			
		||||
no intermediate states are saved.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    >>> regular_input = Variable(torch.randn(1, 3, 227, 227))
 | 
			
		||||
    >>> volatile_input = Variable(torch.randn(1, 3, 227, 227), volatile=True)
 | 
			
		||||
    >>> model = torchvision.models.resnet18(pretrained=True)
 | 
			
		||||
    >>> model(regular_input).requires_grad
 | 
			
		||||
    True
 | 
			
		||||
    >>> model(volatile_input).requires_grad
 | 
			
		||||
    False
 | 
			
		||||
    >>> model(volatile_input).volatile
 | 
			
		||||
    True
 | 
			
		||||
    >>> model(volatile_input).grad_fn is None
 | 
			
		||||
    True
 | 
			
		||||
 | 
			
		||||
How autograd encodes the history
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Autograd is reverse automatic differentiation system.  Conceptually,
 | 
			
		||||
autograd records a graph recording all of the operations that created
 | 
			
		||||
the data as you execute operations, giving you a directed acyclic graph
 | 
			
		||||
whose leaves are the input variables and roots are the output variables.
 | 
			
		||||
By tracing this graph from roots to leaves, you can automatically
 | 
			
		||||
compute the gradients using the chain rule.
 | 
			
		||||
 | 
			
		||||
Internally, autograd represents this graph as a graph of
 | 
			
		||||
:class:`Function` objects (really expressions), which can be
 | 
			
		||||
:meth:`~torch.autograd.Function.apply` ed to compute the result of
 | 
			
		||||
evaluating the graph.  When computing the forwards pass, autograd
 | 
			
		||||
simultaneously performs the requested computations and builds up a graph
 | 
			
		||||
representing the function that computes the gradient (the ``.grad_fn``
 | 
			
		||||
attribute of each :class:`Variable` is an entry point into this graph).
 | 
			
		||||
When the forwards pass is completed, we evaluate this graph in the
 | 
			
		||||
backwards pass to compute the gradients.
 | 
			
		||||
 | 
			
		||||
An important thing to note is that the graph is recreated from scratch at every
 | 
			
		||||
iteration, and this is exactly what allows for using arbitrary Python control
 | 
			
		||||
flow statements, that can change the overall shape and size of the graph at
 | 
			
		||||
every iteration. You don't have to encode all possible paths before you
 | 
			
		||||
launch the training - what you run is what you differentiate.
 | 
			
		||||
 | 
			
		||||
In-place operations on Variables
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Supporting in-place operations in autograd is a hard matter, and we discourage
 | 
			
		||||
their use in most cases. Autograd's aggressive buffer freeing and reuse makes
 | 
			
		||||
it very efficient and there are very few occasions when in-place operations
 | 
			
		||||
actually lower memory usage by any significant amount. Unless you're operating
 | 
			
		||||
under heavy memory pressure, you might never need to use them.
 | 
			
		||||
 | 
			
		||||
There are two main reasons that limit the applicability of in-place operations:
 | 
			
		||||
 | 
			
		||||
1. Overwriting values required to compute gradients. This is why variables don't
 | 
			
		||||
   support ``log_``. Its gradient formula requires the original input, and while
 | 
			
		||||
   it is possible to recreate it by computing the inverse operation, it is
 | 
			
		||||
   numerically unstable, and requires additional work that often defeats the
 | 
			
		||||
   purpose of using these functions.
 | 
			
		||||
 | 
			
		||||
2. Every in-place operation actually requires the implementation to rewrite the
 | 
			
		||||
   computational graph. Out-of-place versions simply allocate new objects and
 | 
			
		||||
   keep references to the old graph, while in-place operations, require
 | 
			
		||||
   changing the creator of all inputs to the :class:`Function` representing
 | 
			
		||||
   this operation. This can be tricky, especially if there are many Variables
 | 
			
		||||
   that reference the same storage (e.g. created by indexing or transposing),
 | 
			
		||||
   and in-place functions will actually raise an error if the storage of
 | 
			
		||||
   modified inputs is referenced by any other :class:`Variable`.
 | 
			
		||||
 | 
			
		||||
In-place correctness checks
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Every variable keeps a version counter, that is incremented every time it's
 | 
			
		||||
marked dirty in any operation. When a Function saves any tensors for backward,
 | 
			
		||||
a version counter of their containing Variable is saved as well. Once you access
 | 
			
		||||
``self.saved_tensors`` it is checked, and if it's greater than the saved value
 | 
			
		||||
an error is raised.
 | 
			
		||||
							
								
								
									
										113
									
								
								docs/source/notes/broadcasting.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								docs/source/notes/broadcasting.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
			
		||||
.. _broadcasting-semantics:
 | 
			
		||||
 | 
			
		||||
Broadcasting semantics
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
Many PyTorch operations support :any:`NumPy Broadcasting Semantics <numpy.doc.broadcasting>`.
 | 
			
		||||
 | 
			
		||||
In short, if a PyTorch operation supports broadcast, then its Tensor arguments can be
 | 
			
		||||
automatically expanded to be of equal sizes (without making copies of the data).
 | 
			
		||||
 | 
			
		||||
General semantics
 | 
			
		||||
-----------------
 | 
			
		||||
Two tensors are "broadcastable" if the following rules hold:
 | 
			
		||||
 | 
			
		||||
- Each tensor has at least one dimension.
 | 
			
		||||
- When iterating over the dimension sizes, starting at the trailing dimension,
 | 
			
		||||
  the dimension sizes must either be equal, one of them is 1, or one of them
 | 
			
		||||
  does not exist.
 | 
			
		||||
 | 
			
		||||
For Example::
 | 
			
		||||
 | 
			
		||||
    >>> x=torch.FloatTensor(5,7,3)
 | 
			
		||||
    >>> y=torch.FloatTensor(5,7,3)
 | 
			
		||||
    # same shapes are always broadcastable (i.e. the above rules always hold)
 | 
			
		||||
 | 
			
		||||
    >>> x=torch.FloatTensor()
 | 
			
		||||
    >>> y=torch.FloatTensor(2,2)
 | 
			
		||||
    # x and y are not broadcastable, because x does not have at least 1 dimension
 | 
			
		||||
 | 
			
		||||
    # can line up trailing dimensions
 | 
			
		||||
    >>> x=torch.FloatTensor(5,3,4,1)
 | 
			
		||||
    >>> y=torch.FloatTensor(  3,1,1)
 | 
			
		||||
    # x and y are broadcastable.
 | 
			
		||||
    # 1st trailing dimension: both have size 1
 | 
			
		||||
    # 2nd trailing dimension: y has size 1
 | 
			
		||||
    # 3rd trailing dimension: x size == y size
 | 
			
		||||
    # 4th trailing dimension: y dimension doesn't exist
 | 
			
		||||
 | 
			
		||||
    # but:
 | 
			
		||||
    >>> x=torch.FloatTensor(5,2,4,1)
 | 
			
		||||
    >>> y=torch.FloatTensor(  3,1,1)
 | 
			
		||||
    # x and y are not broadcastable, because in the 3rd trailing dimension 2 != 3
 | 
			
		||||
 | 
			
		||||
If two tensors :attr:`x`, :attr:`y` are "broadcastable", the resulting tensor size
 | 
			
		||||
is calculated as follows:
 | 
			
		||||
 | 
			
		||||
- If the number of dimensions of :attr:`x` and :attr:`y` are not equal, prepend 1
 | 
			
		||||
  to the dimensions of the tensor with fewer dimensions to make them equal length.
 | 
			
		||||
- Then, for each dimension size, the resulting dimension size is the max of the sizes of
 | 
			
		||||
  :attr:`x` and :attr:`y` along that dimension.
 | 
			
		||||
 | 
			
		||||
For Example::
 | 
			
		||||
 | 
			
		||||
    # can line up trailing dimensions to make reading easier
 | 
			
		||||
    >>> x=torch.FloatTensor(5,1,4,1)
 | 
			
		||||
    >>> y=torch.FloatTensor(  3,1,1)
 | 
			
		||||
    >>> (x+y).size()
 | 
			
		||||
    torch.Size([5, 3, 4, 1])
 | 
			
		||||
 | 
			
		||||
    # but not necessary:
 | 
			
		||||
    >>> x=torch.FloatTensor(1)
 | 
			
		||||
    >>> y=torch.FloatTensor(3,1,7)
 | 
			
		||||
    >>> (x+y).size()
 | 
			
		||||
    torch.Size([3, 1, 7])
 | 
			
		||||
 | 
			
		||||
    >>> x=torch.FloatTensor(5,2,4,1)
 | 
			
		||||
    >>> y=torch.FloatTensor(3,1,1)
 | 
			
		||||
    >>> (x+y).size()
 | 
			
		||||
    RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 1
 | 
			
		||||
 | 
			
		||||
In-place semantics
 | 
			
		||||
------------------
 | 
			
		||||
One complication is that in-place operations do not allow the in-place tensor to change shape
 | 
			
		||||
as a result of the broadcast.
 | 
			
		||||
 | 
			
		||||
For Example::
 | 
			
		||||
 | 
			
		||||
    >>> x=torch.FloatTensor(5,3,4,1)
 | 
			
		||||
    >>> y=torch.FloatTensor(3,1,1)
 | 
			
		||||
    >>> (x.add_(y)).size()
 | 
			
		||||
    torch.Size([5, 3, 4, 1])
 | 
			
		||||
 | 
			
		||||
    # but:
 | 
			
		||||
    >>> x=torch.FloatTensor(1,3,1)
 | 
			
		||||
    >>> y=torch.FloatTensor(3,1,7)
 | 
			
		||||
    >>> (x.add_(y)).size()
 | 
			
		||||
    RuntimeError: The expanded size of the tensor (1) must match the existing size (7) at non-singleton dimension 2.
 | 
			
		||||
 | 
			
		||||
Backwards compatibility
 | 
			
		||||
-----------------------
 | 
			
		||||
Prior versions of PyTorch allowed certain pointwise functions to execute on tensors with different shapes,
 | 
			
		||||
as long as the number of elements in each tensor was equal.  The pointwise operation would then be carried
 | 
			
		||||
out by viewing each tensor as 1-dimensional.  PyTorch now supports broadcasting and the "1-dimensional"
 | 
			
		||||
pointwise behavior is considered deprecated and will generate a Python warning in cases where tensors are
 | 
			
		||||
not broadcastable, but have the same number of elements.
 | 
			
		||||
 | 
			
		||||
Note that the introduction of broadcasting can cause backwards incompatible changes in the case where
 | 
			
		||||
two tensors do not have the same shape, but are broadcastable and have the same number of elements.
 | 
			
		||||
For Example::
 | 
			
		||||
 | 
			
		||||
    >>> torch.add(torch.ones(4,1), torch.randn(4))
 | 
			
		||||
 | 
			
		||||
would previously produce a Tensor with size: torch.Size([4,1]), but now produces a Tensor with size: torch.Size([4,4]).
 | 
			
		||||
In order to help identify cases in your code where backwards incompatibilities introduced by broadcasting may exist,
 | 
			
		||||
you may set `torch.utils.backcompat.broadcast_warning.enabled` to `True`, which will generate a python warning
 | 
			
		||||
in such cases.
 | 
			
		||||
 | 
			
		||||
For Example::
 | 
			
		||||
 | 
			
		||||
    >>> torch.utils.backcompat.broadcast_warning.enabled=True
 | 
			
		||||
    >>> torch.add(torch.ones(4,1), torch.ones(4))
 | 
			
		||||
    __main__:1: UserWarning: self and other do not have the same shape, but are broadcastable, and have the same number of elements.
 | 
			
		||||
    Changing behavior in a backwards incompatible manner to broadcasting rather than viewing as 1-dimensional.
 | 
			
		||||
							
								
								
									
										83
									
								
								docs/source/notes/cuda.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								docs/source/notes/cuda.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
.. _cuda-semantics:
 | 
			
		||||
 | 
			
		||||
CUDA semantics
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
:mod:`torch.cuda` keeps track of currently selected GPU, and all CUDA tensors
 | 
			
		||||
you allocate will be created on it. The selected device can be changed with a
 | 
			
		||||
:any:`torch.cuda.device` context manager.
 | 
			
		||||
 | 
			
		||||
However, once a tensor is allocated, you can do operations on it irrespectively
 | 
			
		||||
of your selected device, and the results will be always placed in on the same
 | 
			
		||||
device as the tensor.
 | 
			
		||||
 | 
			
		||||
Cross-GPU operations are not allowed by default, with the only exception of
 | 
			
		||||
:meth:`~torch.Tensor.copy_`. Unless you enable peer-to-peer memory accesses,
 | 
			
		||||
any attempts to launch ops on tensors spread across different devices will
 | 
			
		||||
raise an error.
 | 
			
		||||
 | 
			
		||||
Below you can find a small example showcasing this::
 | 
			
		||||
 | 
			
		||||
    x = torch.cuda.FloatTensor(1)
 | 
			
		||||
    # x.get_device() == 0
 | 
			
		||||
    y = torch.FloatTensor(1).cuda()
 | 
			
		||||
    # y.get_device() == 0
 | 
			
		||||
 | 
			
		||||
    with torch.cuda.device(1):
 | 
			
		||||
        # allocates a tensor on GPU 1
 | 
			
		||||
        a = torch.cuda.FloatTensor(1)
 | 
			
		||||
 | 
			
		||||
        # transfers a tensor from CPU to GPU 1
 | 
			
		||||
        b = torch.FloatTensor(1).cuda()
 | 
			
		||||
        # a.get_device() == b.get_device() == 1
 | 
			
		||||
 | 
			
		||||
        c = a + b
 | 
			
		||||
        # c.get_device() == 1
 | 
			
		||||
 | 
			
		||||
        z = x + y
 | 
			
		||||
        # z.get_device() == 0
 | 
			
		||||
 | 
			
		||||
        # even within a context, you can give a GPU id to the .cuda call
 | 
			
		||||
        d = torch.randn(2).cuda(2)
 | 
			
		||||
        # d.get_device() == 2
 | 
			
		||||
 | 
			
		||||
Best practices
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Use pinned memory buffers
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
.. warning:
 | 
			
		||||
 | 
			
		||||
    This is an advanced tip. You overuse of pinned memory can cause serious
 | 
			
		||||
    problems if you'll be running low on RAM, and you should be aware that
 | 
			
		||||
    pinning is often an expensive operation.
 | 
			
		||||
 | 
			
		||||
Host to GPU copies are much faster when they originate from pinned (page-locked)
 | 
			
		||||
memory. CPU tensors and storages expose a :meth:`~torch.Tensor.pin_memory`
 | 
			
		||||
method, that returns a copy of the object, with data put in a pinned region.
 | 
			
		||||
 | 
			
		||||
Also, once you pin a tensor or storage, you can use asynchronous GPU copies.
 | 
			
		||||
Just pass an additional ``async=True`` argument to a :meth:`~torch.Tensor.cuda`
 | 
			
		||||
call. This can be used to overlap data transfers with computation.
 | 
			
		||||
 | 
			
		||||
You can make the :class:`~torch.utils.data.DataLoader` return batches placed in
 | 
			
		||||
pinned memory by passing ``pin_memory=True`` to its constructor.
 | 
			
		||||
 | 
			
		||||
.. _cuda-nn-dataparallel-instead:
 | 
			
		||||
 | 
			
		||||
Use nn.DataParallel instead of multiprocessing
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Most use cases involving batched input and multiple GPUs should default to using
 | 
			
		||||
:class:`~torch.nn.DataParallel` to utilize more than one GPU. Even with the GIL,
 | 
			
		||||
a single python process can saturate multiple GPUs.
 | 
			
		||||
 | 
			
		||||
As of version 0.1.9, large numbers of GPUs (8+) might not be fully utilized.
 | 
			
		||||
However, this is a known issue that is under active development. As always,
 | 
			
		||||
test your use case.
 | 
			
		||||
 | 
			
		||||
There are significant caveats to using CUDA models with
 | 
			
		||||
:mod:`~torch.multiprocessing`; unless care is taken to meet the data handling
 | 
			
		||||
requirements exactly, it is likely that your program will have incorrect or
 | 
			
		||||
undefined behavior.
 | 
			
		||||
							
								
								
									
										181
									
								
								docs/source/notes/extending.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								docs/source/notes/extending.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,181 @@
 | 
			
		||||
Extending PyTorch
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
In this note we'll cover ways of extending :mod:`torch.nn`,
 | 
			
		||||
:mod:`torch.autograd`, and writing custom C extensions utilizing our C
 | 
			
		||||
libraries.
 | 
			
		||||
 | 
			
		||||
Extending :mod:`torch.autograd`
 | 
			
		||||
-------------------------------
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torch.autograd
 | 
			
		||||
 | 
			
		||||
Adding operations to :mod:`~torch.autograd` requires implementing a new
 | 
			
		||||
:class:`Function` subclass for each operation. Recall that :class:`Function` s
 | 
			
		||||
are what :mod:`~torch.autograd` uses to compute the results and gradients, and
 | 
			
		||||
encode the operation history. Every new function requires you to implement 2
 | 
			
		||||
methods:
 | 
			
		||||
 | 
			
		||||
- :meth:`~Function.forward` - the code that performs the operation. It can take
 | 
			
		||||
  as many arguments as you want, with some of them being optional, if you
 | 
			
		||||
  specify the default values. All kinds of Python objects are accepted here.
 | 
			
		||||
  :class:`Variable` arguments will be converted to :class:`Tensor` s before the
 | 
			
		||||
  call, and their use will be registered in the graph. Note that this logic won't
 | 
			
		||||
  traverse lists/dicts/any other data structures and will only consider Variables
 | 
			
		||||
  that are direct arguments to the call. You can return either a single
 | 
			
		||||
  :class:`Tensor` output, or a :class:`tuple` of :class:`Tensor` s if there are
 | 
			
		||||
  multiple outputs. Also, please refer to the docs of :class:`Function` to find
 | 
			
		||||
  descriptions of useful methods that can be called only from :meth:`~Function.forward`.
 | 
			
		||||
- :meth:`~Function.backward` - gradient formula. It will be given
 | 
			
		||||
  as many :class:`Variable` arguments as there were outputs, with each of them
 | 
			
		||||
  representing gradient w.r.t. that output. It should return as many
 | 
			
		||||
  :class:`Variable` s as there were inputs, with each of them containing the
 | 
			
		||||
  gradient w.r.t. its corresponding input. If your inputs didn't require
 | 
			
		||||
  gradient (see :attr:`~Variable.needs_input_grad`), or were non-:class:`Variable`
 | 
			
		||||
  objects, you can return :class:`python:None`. Also, if you have optional
 | 
			
		||||
  arguments to :meth:`~Variable.forward` you can return more gradients than there
 | 
			
		||||
  were inputs, as long as they're all :any:`python:None`.
 | 
			
		||||
 | 
			
		||||
Below you can find code for a ``Linear`` function from :mod:`torch.nn`, with
 | 
			
		||||
additional comments::
 | 
			
		||||
 | 
			
		||||
    # Inherit from Function
 | 
			
		||||
    class Linear(Function):
 | 
			
		||||
 | 
			
		||||
        # Note that both forward and backward are @staticmethods
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        # bias is an optional argument
 | 
			
		||||
        def forward(ctx, input, weight, bias=None):
 | 
			
		||||
            ctx.save_for_backward(input, weight, bias)
 | 
			
		||||
            output = input.mm(weight.t())
 | 
			
		||||
            if bias is not None:
 | 
			
		||||
                output += bias.unsqueeze(0).expand_as(output)
 | 
			
		||||
            return output
 | 
			
		||||
 | 
			
		||||
        # This function has only a single output, so it gets only one gradient
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def backward(ctx, grad_output):
 | 
			
		||||
            # This is a pattern that is very convenient - at the top of backward
 | 
			
		||||
            # unpack saved_tensors and initialize all gradients w.r.t. inputs to
 | 
			
		||||
            # None. Thanks to the fact that additional trailing Nones are
 | 
			
		||||
            # ignored, the return statement is simple even when the function has
 | 
			
		||||
            # optional inputs.
 | 
			
		||||
            input, weight, bias = ctx.saved_variables
 | 
			
		||||
            grad_input = grad_weight = grad_bias = None
 | 
			
		||||
 | 
			
		||||
            # These needs_input_grad checks are optional and there only to
 | 
			
		||||
            # improve efficiency. If you want to make your code simpler, you can
 | 
			
		||||
            # skip them. Returning gradients for inputs that don't require it is
 | 
			
		||||
            # not an error.
 | 
			
		||||
            if self.needs_input_grad[0]:
 | 
			
		||||
                grad_input = grad_output.mm(weight)
 | 
			
		||||
            if self.needs_input_grad[1]:
 | 
			
		||||
                grad_weight = grad_output.t().mm(input)
 | 
			
		||||
            if bias is not None and self.needs_input_grad[2]:
 | 
			
		||||
                grad_bias = grad_output.sum(0).squeeze(0)
 | 
			
		||||
 | 
			
		||||
            return grad_input, grad_weight, grad_bias
 | 
			
		||||
 | 
			
		||||
Now, to make it easier to use these custom ops, we recommend aliasing their
 | 
			
		||||
``apply`` method::
 | 
			
		||||
 | 
			
		||||
    linear = Linear.aply
 | 
			
		||||
 | 
			
		||||
Here, we give an additional example of a function that is parametrized by
 | 
			
		||||
non-Variable arguments::
 | 
			
		||||
 | 
			
		||||
    class MulConstant(Function):
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def forward(ctx, tensor, constant):
 | 
			
		||||
            # ctx is a context object that can be used to stash information
 | 
			
		||||
            for backward computation
 | 
			
		||||
            ctx.constant = constant
 | 
			
		||||
            return tensor * constant
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def backward(ctx, grad_output):
 | 
			
		||||
            # We return as many input gradients as there were arguments.
 | 
			
		||||
            # Gradients of non-Tensor arguments to forward must be None.
 | 
			
		||||
            return grad_output * ctx.constant, None
 | 
			
		||||
 | 
			
		||||
You probably want to check if the backward method you implemented actually
 | 
			
		||||
computes the derivatives of your function. It is possible by comparing with
 | 
			
		||||
numerical approximations using small finite differences::
 | 
			
		||||
 | 
			
		||||
    from torch.autograd import gradcheck
 | 
			
		||||
 | 
			
		||||
    # gradchek takes a tuple of tensor as input, check if your gradient
 | 
			
		||||
    # evaluated with these tensors are close enough to numerical
 | 
			
		||||
    # approximations and returns True if they all verify this condition.
 | 
			
		||||
    input = (Variable(torch.randn(20,20).double(), requires_grad=True), Variable(torch.randn(30,20).double(), requires_grad=True),)
 | 
			
		||||
    test = gradcheck(Linear.apply, input, eps=1e-6, atol=1e-4)
 | 
			
		||||
    print(test)
 | 
			
		||||
 | 
			
		||||
Extending :mod:`torch.nn`
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torch.nn
 | 
			
		||||
 | 
			
		||||
:mod:`~torch.nn` exports two kinds of interfaces - modules and their functional
 | 
			
		||||
versions. You can extend it in both ways, but we recommend using modules for
 | 
			
		||||
all kinds of layers, that hold any parameters or buffers, and recommend using
 | 
			
		||||
a functional form parameter-less operations like activation functions, pooling,
 | 
			
		||||
etc.
 | 
			
		||||
 | 
			
		||||
Adding a functional version of an operation is already fully covered in the
 | 
			
		||||
section above.
 | 
			
		||||
 | 
			
		||||
Adding a :class:`Module`
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Since :mod:`~torch.nn` heavily utilizes :mod:`~torch.autograd`, adding a new
 | 
			
		||||
:class:`Module` requires implementing a :class:`~torch.autograd.Function`
 | 
			
		||||
that performs the operation and can compute the gradient. From now on let's
 | 
			
		||||
assume that we want to implement a ``Linear`` module and we have the function
 | 
			
		||||
implementated as in the listing above. There's very little code required to
 | 
			
		||||
add this. Now, there are two functions that need to be implemented:
 | 
			
		||||
 | 
			
		||||
- ``__init__`` (*optional*) - takes in arguments such as kernel sizes, numbers
 | 
			
		||||
  of features, etc. and initializes parameters and buffers.
 | 
			
		||||
- :meth:`~Module.forward` - instantiates a :class:`~torch.autograd.Function` and
 | 
			
		||||
  uses it to perform the operation. It's very similar to a functional wrapper
 | 
			
		||||
  shown above.
 | 
			
		||||
 | 
			
		||||
This is how a ``Linear`` module can be implemented::
 | 
			
		||||
 | 
			
		||||
    class Linear(nn.Module):
 | 
			
		||||
        def __init__(self, input_features, output_features, bias=True):
 | 
			
		||||
            self.input_features = input_features
 | 
			
		||||
            self.output_features = output_features
 | 
			
		||||
 | 
			
		||||
            # nn.Parameter is a special kind of Variable, that will get
 | 
			
		||||
            # automatically registered as Module's parameter once it's assigned
 | 
			
		||||
            # as an attribute. Parameters and buffers need to be registered, or
 | 
			
		||||
            # they won't appear in .parameters() (doesn't apply to buffers), and
 | 
			
		||||
            # won't be converted when e.g. .cuda() is called. You can use
 | 
			
		||||
            # .register_buffer() to register buffers.
 | 
			
		||||
            # nn.Parameters can never be volatile and, different than Variables,
 | 
			
		||||
            # they require gradients by default.
 | 
			
		||||
            self.weight = nn.Parameter(torch.Tensor(input_features, output_features))
 | 
			
		||||
            if bias:
 | 
			
		||||
                self.bias = nn.Parameter(torch.Tensor(output_features))
 | 
			
		||||
            else:
 | 
			
		||||
                # You should always register all possible parameters, but the
 | 
			
		||||
                # optional ones can be None if you want.
 | 
			
		||||
                self.register_parameter('bias', None)
 | 
			
		||||
 | 
			
		||||
            # Not a very smart way to initialize weights
 | 
			
		||||
            self.weight.data.uniform_(-0.1, 0.1)
 | 
			
		||||
            if bias is not None:
 | 
			
		||||
                self.bias.data.uniform_(-0.1, 0.1)
 | 
			
		||||
 | 
			
		||||
        def forward(self, input):
 | 
			
		||||
            # See the autograd section for explanation of what happens here.
 | 
			
		||||
            return Linear()(input, self.weight, self.bias)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Writing custom C extensions
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
Coming soon. For now you can find an example at
 | 
			
		||||
`GitHub <https://github.com/pytorch/extension-ffi>`_.
 | 
			
		||||
							
								
								
									
										124
									
								
								docs/source/notes/multiprocessing.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								docs/source/notes/multiprocessing.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,124 @@
 | 
			
		||||
Multiprocessing best practices
 | 
			
		||||
==============================
 | 
			
		||||
 | 
			
		||||
:mod:`torch.multiprocessing` is a drop in replacement for Python's
 | 
			
		||||
:mod:`python:multiprocessing` module. It supports the exact same operations,
 | 
			
		||||
but extends it, so that all tensors sent through a
 | 
			
		||||
:class:`python:multiprocessing.Queue`, will have their data moved into shared
 | 
			
		||||
memory and will only send a handle to another process.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    When a :class:`~torch.autograd.Variable` is sent to another process, both
 | 
			
		||||
    the :attr:`Variable.data` and :attr:`Variable.grad.data` are going to be
 | 
			
		||||
    shared.
 | 
			
		||||
 | 
			
		||||
This allows to implement various training methods, like Hogwild, A3C, or any
 | 
			
		||||
others that require asynchronous operation.
 | 
			
		||||
 | 
			
		||||
Sharing CUDA tensors
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
Sharing CUDA tensors between processes is supported only in Python 3, using
 | 
			
		||||
a ``spawn`` or ``forkserver`` start methods. :mod:`python:multiprocessing` in
 | 
			
		||||
Python 2 can only create subprocesses using ``fork``, and it's not supported
 | 
			
		||||
by the CUDA runtime.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    CUDA API requires that the allocation exported to other processes remains
 | 
			
		||||
    valid as long as it's used by them. You should be careful and ensure that
 | 
			
		||||
    CUDA tensors you shared don't go out of scope as long as it's necessary.
 | 
			
		||||
    This shouldn't be a problem for sharing model parameters, but passing other
 | 
			
		||||
    kinds of data should be done with care. Note that this restriction doesn't
 | 
			
		||||
    apply to shared CPU memory.
 | 
			
		||||
 | 
			
		||||
See also: :ref:`cuda-nn-dataparallel-instead`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Best practices and tips
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
Avoiding and fighting deadlocks
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
There are a lot of things that can go wrong when a new process is spawned, with
 | 
			
		||||
the most common cause of deadlocks being background threads. If there's any
 | 
			
		||||
thread that holds a lock or imports a module, and ``fork`` is called, it's very
 | 
			
		||||
likely that the subprocess will be in a corrupted state and will deadlock or
 | 
			
		||||
fail in a different way. Note that even if you don't, Python built in
 | 
			
		||||
libraries do - no need to look further than :mod:`python:multiprocessing`.
 | 
			
		||||
:class:`python:multiprocessing.Queue` is actually a very complex class, that
 | 
			
		||||
spawns multiple threads used to serialize, send and receive objects, and they
 | 
			
		||||
can cause aforementioned problems too. If you find yourself in such situation
 | 
			
		||||
try using a :class:`~python:multiprocessing.queues.SimpleQueue`, that doesn't
 | 
			
		||||
use any additional threads.
 | 
			
		||||
 | 
			
		||||
We're trying our best to make it easy for you and ensure these deadlocks don't
 | 
			
		||||
happen but some things are out of our control. If you have any issues you can't
 | 
			
		||||
cope with for a while, try reaching out on forums, and we'll see if it's an
 | 
			
		||||
issue we can fix.
 | 
			
		||||
 | 
			
		||||
Reuse buffers passed through a Queue
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Remember that each time you put a :class:`~torch.Tensor` into a
 | 
			
		||||
:class:`python:multiprocessing.Queue`, it has to be moved into shared memory.
 | 
			
		||||
If it's already shared, it is a no-op, otherwise it will incur an additional
 | 
			
		||||
memory copy that can slow down the whole process. Even if you have a pool of
 | 
			
		||||
processes sending data to a single one, make it send the buffers back - this
 | 
			
		||||
is nearly free and will let you avoid a copy when sending next batch.
 | 
			
		||||
 | 
			
		||||
Asynchronous multiprocess training (e.g. Hogwild)
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
Using :mod:`torch.multiprocessing`, it is possible to train a model
 | 
			
		||||
asynchronously, with parameters either shared all the time, or being
 | 
			
		||||
periodically synchronized. In the first case, we recommend sending over the whole
 | 
			
		||||
model object, while in the latter, we advise to only send the
 | 
			
		||||
:meth:`~torch.nn.Module.state_dict`.
 | 
			
		||||
 | 
			
		||||
We recommend using :class:`python:multiprocessing.Queue` for passing all kinds
 | 
			
		||||
of PyTorch objects between processes. It is possible to e.g. inherit the tensors
 | 
			
		||||
and storages already in shared memory, when using the ``fork`` start method,
 | 
			
		||||
however it is very bug prone and should be used with care, and only by advanced
 | 
			
		||||
users. Queues, even though they're sometimes a less elegant solution, will work
 | 
			
		||||
properly in all cases.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    You should be careful about having global statements, that are not guarded
 | 
			
		||||
    with an ``if __name__ == '__main__'``. If a different start method than
 | 
			
		||||
    ``fork`` is used, they will be executed in all subprocesses.
 | 
			
		||||
 | 
			
		||||
Hogwild
 | 
			
		||||
~~~~~~~
 | 
			
		||||
 | 
			
		||||
A concrete Hogwild implementation can be found in the `examples repository`__,
 | 
			
		||||
but to showcase the overall structure of the code, there's also a minimal
 | 
			
		||||
example below as well::
 | 
			
		||||
 | 
			
		||||
    import torch.multiprocessing as mp
 | 
			
		||||
    from model import MyModel
 | 
			
		||||
 | 
			
		||||
    def train(model):
 | 
			
		||||
        # Construct data_loader, optimizer, etc.
 | 
			
		||||
        for data, labels in data_loader:
 | 
			
		||||
            optimizer.zero_grad()
 | 
			
		||||
            loss_fn(model(data), labels).backward()
 | 
			
		||||
            optimizer.step()  # This will update the shared parameters
 | 
			
		||||
 | 
			
		||||
    if __name__ == '__main__':
 | 
			
		||||
        num_processes = 4
 | 
			
		||||
        model = MyModel()
 | 
			
		||||
        # NOTE: this is required for the ``fork`` method to work
 | 
			
		||||
        model.share_memory()
 | 
			
		||||
        processes = []
 | 
			
		||||
        for rank in range(num_processes):
 | 
			
		||||
            p = mp.Process(target=train, args=(model,))
 | 
			
		||||
            p.start()
 | 
			
		||||
            processes.append(p)
 | 
			
		||||
        for p in processes:
 | 
			
		||||
          p.join()
 | 
			
		||||
 | 
			
		||||
.. __: https://github.com/pytorch/examples/tree/master/mnist_hogwild
 | 
			
		||||
							
								
								
									
										34
									
								
								docs/source/notes/serialization.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								docs/source/notes/serialization.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
 | 
			
		||||
Serialization semantics
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
Best practices
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
.. _recommend-saving-models:
 | 
			
		||||
 | 
			
		||||
Recommended approach for saving a model
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
There are two main approaches for serializing and restoring a model.
 | 
			
		||||
 | 
			
		||||
The first (recommended) saves and loads only the model parameters::
 | 
			
		||||
 | 
			
		||||
    torch.save(the_model.state_dict(), PATH)
 | 
			
		||||
 | 
			
		||||
Then later::
 | 
			
		||||
 | 
			
		||||
    the_model = TheModelClass(*args, **kwargs)
 | 
			
		||||
    the_model.load_state_dict(torch.load(PATH))
 | 
			
		||||
 | 
			
		||||
The second saves and loads the entire model::
 | 
			
		||||
 | 
			
		||||
    torch.save(the_model, PATH)
 | 
			
		||||
 | 
			
		||||
Then later::
 | 
			
		||||
 | 
			
		||||
    the_model = torch.load(PATH)
 | 
			
		||||
 | 
			
		||||
However in this case, the serialized data is bound to the specific classes
 | 
			
		||||
and the exact directory structure used, so it can break in various ways when
 | 
			
		||||
used in other projects, or after some serious refactors.
 | 
			
		||||
							
								
								
									
										134
									
								
								docs/source/optim.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								docs/source/optim.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,134 @@
 | 
			
		||||
torch.optim
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
.. automodule:: torch.optim
 | 
			
		||||
 | 
			
		||||
How to use an optimizer
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
To use :mod:`torch.optim` you have to construct an optimizer object, that will hold
 | 
			
		||||
the current state and will update the parameters based on the computed gradients.
 | 
			
		||||
 | 
			
		||||
Constructing it
 | 
			
		||||
^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
To construct an :class:`Optimizer` you have to give it an iterable containing the
 | 
			
		||||
parameters (all should be :class:`~torch.autograd.Variable` s) to optimize. Then,
 | 
			
		||||
you can specify optimizer-specific options such as the learning rate, weight decay, etc.
 | 
			
		||||
 | 
			
		||||
Example::
 | 
			
		||||
 | 
			
		||||
    optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)
 | 
			
		||||
    optimizer = optim.Adam([var1, var2], lr = 0.0001)
 | 
			
		||||
 | 
			
		||||
Per-parameter options
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
:class:`Optimizer` s also support specifying per-parameter options. To do this, instead
 | 
			
		||||
of passing an iterable of :class:`~torch.autograd.Variable` s, pass in an iterable of
 | 
			
		||||
:class:`dict` s. Each of them will define a separate parameter group, and should contain
 | 
			
		||||
a ``params`` key, containing a list of parameters belonging to it. Other keys
 | 
			
		||||
should match the keyword arguments accepted by the optimizers, and will be used
 | 
			
		||||
as optimization options for this group.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    You can still pass options as keyword arguments. They will be used as
 | 
			
		||||
    defaults, in the groups that didn't override them. This is useful when you
 | 
			
		||||
    only want to vary a single option, while keeping all others consistent
 | 
			
		||||
    between parameter groups.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
For example, this is very useful when one wants to specify per-layer learning rates::
 | 
			
		||||
 | 
			
		||||
    optim.SGD([
 | 
			
		||||
                    {'params': model.base.parameters()},
 | 
			
		||||
                    {'params': model.classifier.parameters(), 'lr': 1e-3}
 | 
			
		||||
                ], lr=1e-2, momentum=0.9)
 | 
			
		||||
 | 
			
		||||
This means that ``model.base``'s parameters will use the default learning rate of ``1e-2``,
 | 
			
		||||
``model.classifier``'s parameters will use a learning rate of ``1e-3``, and a momentum of
 | 
			
		||||
``0.9`` will be used for all parameters
 | 
			
		||||
 | 
			
		||||
Taking an optimization step
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
All optimizers implement a :func:`~Optimizer.step` method, that updates the
 | 
			
		||||
parameters. It can be used in two ways:
 | 
			
		||||
 | 
			
		||||
``optimizer.step()``
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
This is a simplified version supported by most optimizers. The function can be
 | 
			
		||||
called once the gradients are computed using e.g.
 | 
			
		||||
:func:`~torch.autograd.Variable.backward`.
 | 
			
		||||
 | 
			
		||||
Example::
 | 
			
		||||
 | 
			
		||||
    for input, target in dataset:
 | 
			
		||||
        optimizer.zero_grad()
 | 
			
		||||
        output = model(input)
 | 
			
		||||
        loss = loss_fn(output, target)
 | 
			
		||||
        loss.backward()
 | 
			
		||||
        optimizer.step()
 | 
			
		||||
 | 
			
		||||
``optimizer.step(closure)``
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
Some optimization algorithms such as Conjugate Gradient and LBFGS need to
 | 
			
		||||
reevaluate the function multiple times, so you have to pass in a closure that
 | 
			
		||||
allows them to recompute your model. The closure should clear the gradients,
 | 
			
		||||
compute the loss, and return it.
 | 
			
		||||
 | 
			
		||||
Example::
 | 
			
		||||
 | 
			
		||||
    for input, target in dataset:
 | 
			
		||||
        def closure():
 | 
			
		||||
            optimizer.zero_grad()
 | 
			
		||||
            output = model(input)
 | 
			
		||||
            loss = loss_fn(output, target)
 | 
			
		||||
            loss.backward()
 | 
			
		||||
            return loss
 | 
			
		||||
        optimizer.step(closure)
 | 
			
		||||
 | 
			
		||||
Algorithms
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Optimizer
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: Adadelta
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: Adagrad
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: Adam
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: Adamax
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: ASGD
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: LBFGS
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: RMSprop
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: Rprop
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: SGD
 | 
			
		||||
    :members:
 | 
			
		||||
 | 
			
		||||
How to adjust Learning Rate
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
:mod:`torch.optim.lr_scheduler` provides several methods to adjust the learning
 | 
			
		||||
rate based on the number of epoches. :class:`torch.optim.lr_scheduler.ReduceLROnPlateau`
 | 
			
		||||
allows dynamic learning rate reducing based on some validation measurements.
 | 
			
		||||
 | 
			
		||||
.. autoclass:: torch.optim.lr_scheduler.LambdaLR
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: torch.optim.lr_scheduler.StepLR
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: torch.optim.lr_scheduler.MultiStepLR
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: torch.optim.lr_scheduler.ExponentialLR
 | 
			
		||||
    :members:
 | 
			
		||||
.. autoclass:: torch.optim.lr_scheduler.ReduceLROnPlateau
 | 
			
		||||
    :members:
 | 
			
		||||
							
								
								
									
										114
									
								
								docs/source/sparse.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								docs/source/sparse.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,114 @@
 | 
			
		||||
.. currentmodule:: torch.sparse
 | 
			
		||||
 | 
			
		||||
torch.sparse
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    This API is currently experimental and may change in the near future.
 | 
			
		||||
 | 
			
		||||
Torch supports sparse tensors in COO(rdinate) format, which can
 | 
			
		||||
efficiently store and process tensors for which the majority of elements
 | 
			
		||||
are zeros.
 | 
			
		||||
 | 
			
		||||
A sparse tensor is represented as a pair of dense tensors: a tensor
 | 
			
		||||
of values and a tensor of indices.  A sparse tensor can be constructed
 | 
			
		||||
by providing these two tensors, as well as the size of the sparse tensor
 | 
			
		||||
(which cannot be inferred from these tensors!)
 | 
			
		||||
 | 
			
		||||
    >>> i = torch.LongTensor([[0, 1], [2, 0]])
 | 
			
		||||
    >>> v = torch.FloatTensor([3, 4])
 | 
			
		||||
    >>> torch.sparse.FloatTensor(i, v, torch.Size([2,3])).to_dense()
 | 
			
		||||
     0  0  3
 | 
			
		||||
     4  0  0
 | 
			
		||||
    [torch.FloatTensor of size 2x2]
 | 
			
		||||
 | 
			
		||||
You can also construct hybrid sparse tensors, where only the first n
 | 
			
		||||
dimensions are sparse, and the rest of the dimensions are dense.
 | 
			
		||||
 | 
			
		||||
    >>> i = torch.LongTensor([[2, 4]])
 | 
			
		||||
    >>> v = torch.FloatTensor([[1, 3], [5, 7]])
 | 
			
		||||
    >>> torch.sparse.FloatTensor(i, v).to_dense()
 | 
			
		||||
     0  0
 | 
			
		||||
     0  0
 | 
			
		||||
     1  3
 | 
			
		||||
     0  0
 | 
			
		||||
     5  7
 | 
			
		||||
    [torch.FloatTensor of size 5x2]
 | 
			
		||||
 | 
			
		||||
An empty sparse tensor can be constructed by specifying its size:
 | 
			
		||||
 | 
			
		||||
    >>> torch.sparse.FloatTensor(2, 3)
 | 
			
		||||
    SparseFloatTensor of size 2x3 with indices:
 | 
			
		||||
    [torch.LongTensor with no dimension]
 | 
			
		||||
    and values:
 | 
			
		||||
    [torch.FloatTensor with no dimension]
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    Our sparse tensor format permits *uncoalesced* sparse tensors, where
 | 
			
		||||
    there may be duplicate coordinates in the indices; in this case,
 | 
			
		||||
    the interpretation is that the value at that index is the sum of all
 | 
			
		||||
    duplicate value entries. Uncoalesced tensors permit us to implement
 | 
			
		||||
    certain operators more efficiently.
 | 
			
		||||
 | 
			
		||||
    For the most part, you shouldn't have to care whether or not a
 | 
			
		||||
    sparse tensor is coalesced or not, as most operations will work
 | 
			
		||||
    identically given a coalesced or uncoalesced sparse tensor.
 | 
			
		||||
    However, there are two cases in which you may need to care.
 | 
			
		||||
 | 
			
		||||
    First, if you repeatedly perform an operation that can produce
 | 
			
		||||
    duplicate entries (e.g., :func:`torch.sparse.FloatTensor.add`), you
 | 
			
		||||
    should occasionally coalesce your sparse tensors to prevent
 | 
			
		||||
    them from growing too large.
 | 
			
		||||
 | 
			
		||||
    Second, some operators will produce different values depending on
 | 
			
		||||
    whether or not they are coalesced or not (e.g.,
 | 
			
		||||
    :func:`torch.sparse.FloatTensor._values` and
 | 
			
		||||
    :func:`torch.sparse.FloatTensor._indices`, as well as
 | 
			
		||||
    :func:`torch.Tensor._sparse_mask`).  These operators are
 | 
			
		||||
    prefixed by an underscore to indicate that they reveal internal
 | 
			
		||||
    implementation details and should be used with care, since code
 | 
			
		||||
    that works with coalesced sparse tensors may not work with
 | 
			
		||||
    uncoalesced sparse tensors; generally speaking, it is safest
 | 
			
		||||
    to explicitly coalesce before working with these operators.
 | 
			
		||||
 | 
			
		||||
    For example, suppose that we wanted to implement an operator
 | 
			
		||||
    by operating directly on :func:`torch.sparse.FloatTensor._values`.
 | 
			
		||||
    Multiplication by a scalar can be implemented in the obvious way,
 | 
			
		||||
    as multiplication distributes over addition; however, square root
 | 
			
		||||
    cannot be implemented directly, since ``sqrt(a + b) != sqrt(a) +
 | 
			
		||||
    sqrt(b)`` (which is what would be computed if you were given an
 | 
			
		||||
    uncoalesced tensor.)
 | 
			
		||||
 | 
			
		||||
.. class:: FloatTensor()
 | 
			
		||||
 | 
			
		||||
    .. method:: add
 | 
			
		||||
    .. method:: add_
 | 
			
		||||
    .. method:: clone
 | 
			
		||||
    .. method:: dim
 | 
			
		||||
    .. method:: div
 | 
			
		||||
    .. method:: div_
 | 
			
		||||
    .. method:: get_device
 | 
			
		||||
    .. method:: hspmm
 | 
			
		||||
    .. method:: mm
 | 
			
		||||
    .. method:: mul
 | 
			
		||||
    .. method:: mul_
 | 
			
		||||
    .. method:: resizeAs_
 | 
			
		||||
    .. method:: size
 | 
			
		||||
    .. method:: spadd
 | 
			
		||||
    .. method:: spmm
 | 
			
		||||
    .. method:: sspaddmm
 | 
			
		||||
    .. method:: sspmm
 | 
			
		||||
    .. method:: sub
 | 
			
		||||
    .. method:: sub_
 | 
			
		||||
    .. method:: t_
 | 
			
		||||
    .. method:: toDense
 | 
			
		||||
    .. method:: transpose
 | 
			
		||||
    .. method:: transpose_
 | 
			
		||||
    .. method:: zero_
 | 
			
		||||
    .. method:: coalesce
 | 
			
		||||
    .. method:: is_coalesced
 | 
			
		||||
    .. method:: _indices
 | 
			
		||||
    .. method:: _values
 | 
			
		||||
    .. method:: _nnz
 | 
			
		||||
							
								
								
									
										12
									
								
								docs/source/storage.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								docs/source/storage.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
torch.Storage
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
A :class:`torch.Storage` is a contiguous, one-dimensional array of a single
 | 
			
		||||
data type.
 | 
			
		||||
 | 
			
		||||
Every :class:`torch.Tensor` has a corresponding storage of the same data type.
 | 
			
		||||
 | 
			
		||||
.. autoclass:: torch.FloatStorage
 | 
			
		||||
   :members:
 | 
			
		||||
   :undoc-members:
 | 
			
		||||
   :inherited-members:
 | 
			
		||||
							
								
								
									
										309
									
								
								docs/source/tensors.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								docs/source/tensors.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,309 @@
 | 
			
		||||
.. currentmodule:: torch
 | 
			
		||||
 | 
			
		||||
torch.Tensor
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
A :class:`torch.Tensor` is a multi-dimensional matrix containing elements of
 | 
			
		||||
a single data type.
 | 
			
		||||
 | 
			
		||||
Torch defines seven CPU tensor types and eight GPU tensor types:
 | 
			
		||||
 | 
			
		||||
======================== ===========================   ================================
 | 
			
		||||
Data type                CPU tensor                    GPU tensor
 | 
			
		||||
======================== ===========================   ================================
 | 
			
		||||
32-bit floating point    :class:`torch.FloatTensor`    :class:`torch.cuda.FloatTensor`
 | 
			
		||||
64-bit floating point    :class:`torch.DoubleTensor`   :class:`torch.cuda.DoubleTensor`
 | 
			
		||||
16-bit floating point    :class:`torch.HalfTensor`     :class:`torch.cuda.HalfTensor`
 | 
			
		||||
8-bit integer (unsigned) :class:`torch.ByteTensor`     :class:`torch.cuda.ByteTensor`
 | 
			
		||||
8-bit integer (signed)   :class:`torch.CharTensor`     :class:`torch.cuda.CharTensor`
 | 
			
		||||
16-bit integer (signed)  :class:`torch.ShortTensor`    :class:`torch.cuda.ShortTensor`
 | 
			
		||||
32-bit integer (signed)  :class:`torch.IntTensor`      :class:`torch.cuda.IntTensor`
 | 
			
		||||
64-bit integer (signed)  :class:`torch.LongTensor`     :class:`torch.cuda.LongTensor`
 | 
			
		||||
======================== ===========================   ================================
 | 
			
		||||
 | 
			
		||||
The :class:`torch.Tensor` constructor is an alias for the default tensor type
 | 
			
		||||
(:class:`torch.FloatTensor`).
 | 
			
		||||
 | 
			
		||||
A tensor can be constructed from a Python :class:`list` or sequence:
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
    >>> torch.FloatTensor([[1, 2, 3], [4, 5, 6]])
 | 
			
		||||
    1  2  3
 | 
			
		||||
    4  5  6
 | 
			
		||||
    [torch.FloatTensor of size 2x3]
 | 
			
		||||
 | 
			
		||||
An empty tensor can be constructed by specifying its size:
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
    >>> torch.IntTensor(2, 4).zero_()
 | 
			
		||||
    0  0  0  0
 | 
			
		||||
    0  0  0  0
 | 
			
		||||
    [torch.IntTensor of size 2x4]
 | 
			
		||||
 | 
			
		||||
The contents of a tensor can be accessed and modified using Python's indexing
 | 
			
		||||
and slicing notation:
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
    >>> x = torch.FloatTensor([[1, 2, 3], [4, 5, 6]])
 | 
			
		||||
    >>> print(x[1][2])
 | 
			
		||||
    6.0
 | 
			
		||||
    >>> x[0][1] = 8
 | 
			
		||||
    >>> print(x)
 | 
			
		||||
     1  8  3
 | 
			
		||||
     4  5  6
 | 
			
		||||
    [torch.FloatTensor of size 2x3]
 | 
			
		||||
 | 
			
		||||
Each tensor has an associated :class:`torch.Storage`, which holds its data.
 | 
			
		||||
The tensor class provides multi-dimensional, `strided <https://en.wikipedia.org/wiki/Stride_of_an_array>`_
 | 
			
		||||
view of a storage and defines numeric operations on it.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
   Methods which mutate a tensor are marked with an underscore suffix.
 | 
			
		||||
   For example, :func:`torch.FloatTensor.abs_` computes the absolute value
 | 
			
		||||
   in-place and returns the modified tensor, while :func:`torch.FloatTensor.abs`
 | 
			
		||||
   computes the result in a new tensor.
 | 
			
		||||
 | 
			
		||||
.. class:: Tensor()
 | 
			
		||||
           Tensor(*sizes)
 | 
			
		||||
           Tensor(size)
 | 
			
		||||
           Tensor(sequence)
 | 
			
		||||
           Tensor(ndarray)
 | 
			
		||||
           Tensor(tensor)
 | 
			
		||||
           Tensor(storage)
 | 
			
		||||
 | 
			
		||||
   Creates a new tensor from an optional size or data.
 | 
			
		||||
 | 
			
		||||
   If no arguments are given, an empty zero-dimensional tensor is returned.
 | 
			
		||||
   If a :class:`numpy.ndarray`, :class:`torch.Tensor`, or :class:`torch.Storage`
 | 
			
		||||
   is given, a new tensor that shares the same data is returned. If a Python
 | 
			
		||||
   sequence is given, a new tensor is created from a copy of the sequence.
 | 
			
		||||
 | 
			
		||||
   .. automethod:: abs
 | 
			
		||||
   .. automethod:: abs_
 | 
			
		||||
   .. automethod:: acos
 | 
			
		||||
   .. automethod:: acos_
 | 
			
		||||
   .. automethod:: add
 | 
			
		||||
   .. automethod:: add_
 | 
			
		||||
   .. automethod:: addbmm
 | 
			
		||||
   .. automethod:: addbmm_
 | 
			
		||||
   .. automethod:: addcdiv
 | 
			
		||||
   .. automethod:: addcdiv_
 | 
			
		||||
   .. automethod:: addcmul
 | 
			
		||||
   .. automethod:: addcmul_
 | 
			
		||||
   .. automethod:: addmm
 | 
			
		||||
   .. automethod:: addmm_
 | 
			
		||||
   .. automethod:: addmv
 | 
			
		||||
   .. automethod:: addmv_
 | 
			
		||||
   .. automethod:: addr
 | 
			
		||||
   .. automethod:: addr_
 | 
			
		||||
   .. automethod:: apply_
 | 
			
		||||
   .. automethod:: asin
 | 
			
		||||
   .. automethod:: asin_
 | 
			
		||||
   .. automethod:: atan
 | 
			
		||||
   .. automethod:: atan2
 | 
			
		||||
   .. automethod:: atan2_
 | 
			
		||||
   .. automethod:: atan_
 | 
			
		||||
   .. automethod:: baddbmm
 | 
			
		||||
   .. automethod:: baddbmm_
 | 
			
		||||
   .. automethod:: bernoulli
 | 
			
		||||
   .. automethod:: bernoulli_
 | 
			
		||||
   .. automethod:: bmm
 | 
			
		||||
   .. automethod:: byte
 | 
			
		||||
   .. automethod:: cauchy_
 | 
			
		||||
   .. automethod:: ceil
 | 
			
		||||
   .. automethod:: ceil_
 | 
			
		||||
   .. automethod:: char
 | 
			
		||||
   .. automethod:: chunk
 | 
			
		||||
   .. automethod:: clamp
 | 
			
		||||
   .. automethod:: clamp_
 | 
			
		||||
   .. automethod:: clone
 | 
			
		||||
   .. automethod:: contiguous
 | 
			
		||||
   .. automethod:: copy_
 | 
			
		||||
   .. automethod:: cos
 | 
			
		||||
   .. automethod:: cos_
 | 
			
		||||
   .. automethod:: cosh
 | 
			
		||||
   .. automethod:: cosh_
 | 
			
		||||
   .. automethod:: cpu
 | 
			
		||||
   .. automethod:: cross
 | 
			
		||||
   .. automethod:: cuda
 | 
			
		||||
   .. automethod:: cumprod
 | 
			
		||||
   .. automethod:: cumsum
 | 
			
		||||
   .. automethod:: data_ptr
 | 
			
		||||
   .. automethod:: diag
 | 
			
		||||
   .. automethod:: dim
 | 
			
		||||
   .. automethod:: dist
 | 
			
		||||
   .. automethod:: div
 | 
			
		||||
   .. automethod:: div_
 | 
			
		||||
   .. automethod:: dot
 | 
			
		||||
   .. automethod:: double
 | 
			
		||||
   .. automethod:: eig
 | 
			
		||||
   .. automethod:: element_size
 | 
			
		||||
   .. automethod:: eq
 | 
			
		||||
   .. automethod:: eq_
 | 
			
		||||
   .. automethod:: equal
 | 
			
		||||
   .. automethod:: exp
 | 
			
		||||
   .. automethod:: exp_
 | 
			
		||||
   .. automethod:: expand
 | 
			
		||||
   .. automethod:: expand_as
 | 
			
		||||
   .. automethod:: exponential_
 | 
			
		||||
   .. automethod:: fill_
 | 
			
		||||
   .. automethod:: float
 | 
			
		||||
   .. automethod:: floor
 | 
			
		||||
   .. automethod:: floor_
 | 
			
		||||
   .. automethod:: fmod
 | 
			
		||||
   .. automethod:: fmod_
 | 
			
		||||
   .. automethod:: frac
 | 
			
		||||
   .. automethod:: frac_
 | 
			
		||||
   .. automethod:: gather
 | 
			
		||||
   .. automethod:: ge
 | 
			
		||||
   .. automethod:: ge_
 | 
			
		||||
   .. automethod:: gels
 | 
			
		||||
   .. automethod:: geometric_
 | 
			
		||||
   .. automethod:: geqrf
 | 
			
		||||
   .. automethod:: ger
 | 
			
		||||
   .. automethod:: gesv
 | 
			
		||||
   .. automethod:: gt
 | 
			
		||||
   .. automethod:: gt_
 | 
			
		||||
   .. automethod:: half
 | 
			
		||||
   .. automethod:: histc
 | 
			
		||||
   .. automethod:: index
 | 
			
		||||
   .. automethod:: index_add_
 | 
			
		||||
   .. automethod:: index_copy_
 | 
			
		||||
   .. automethod:: index_fill_
 | 
			
		||||
   .. automethod:: index_select
 | 
			
		||||
   .. automethod:: int
 | 
			
		||||
   .. automethod:: inverse
 | 
			
		||||
   .. automethod:: is_contiguous
 | 
			
		||||
   .. autoattribute:: is_cuda
 | 
			
		||||
      :annotation:
 | 
			
		||||
   .. automethod:: is_pinned
 | 
			
		||||
   .. automethod:: is_set_to
 | 
			
		||||
   .. automethod:: is_signed
 | 
			
		||||
   .. automethod:: kthvalue
 | 
			
		||||
   .. automethod:: le
 | 
			
		||||
   .. automethod:: le_
 | 
			
		||||
   .. automethod:: lerp
 | 
			
		||||
   .. automethod:: lerp_
 | 
			
		||||
   .. automethod:: log
 | 
			
		||||
   .. automethod:: log1p
 | 
			
		||||
   .. automethod:: log1p_
 | 
			
		||||
   .. automethod:: log_
 | 
			
		||||
   .. automethod:: log_normal_
 | 
			
		||||
   .. automethod:: long
 | 
			
		||||
   .. automethod:: lt
 | 
			
		||||
   .. automethod:: lt_
 | 
			
		||||
   .. automethod:: map_
 | 
			
		||||
   .. automethod:: masked_scatter_
 | 
			
		||||
   .. automethod:: masked_fill_
 | 
			
		||||
   .. automethod:: masked_select
 | 
			
		||||
   .. automethod:: matmul
 | 
			
		||||
   .. automethod:: max
 | 
			
		||||
   .. automethod:: mean
 | 
			
		||||
   .. automethod:: median
 | 
			
		||||
   .. automethod:: min
 | 
			
		||||
   .. automethod:: mm
 | 
			
		||||
   .. automethod:: mode
 | 
			
		||||
   .. automethod:: mul
 | 
			
		||||
   .. automethod:: mul_
 | 
			
		||||
   .. automethod:: multinomial
 | 
			
		||||
   .. automethod:: mv
 | 
			
		||||
   .. automethod:: narrow
 | 
			
		||||
   .. automethod:: ndimension
 | 
			
		||||
   .. automethod:: ne
 | 
			
		||||
   .. automethod:: ne_
 | 
			
		||||
   .. automethod:: neg
 | 
			
		||||
   .. automethod:: neg_
 | 
			
		||||
   .. automethod:: nelement
 | 
			
		||||
   .. automethod:: new
 | 
			
		||||
   .. automethod:: nonzero
 | 
			
		||||
   .. automethod:: norm
 | 
			
		||||
   .. automethod:: normal_
 | 
			
		||||
   .. automethod:: numel
 | 
			
		||||
   .. automethod:: numpy
 | 
			
		||||
   .. automethod:: orgqr
 | 
			
		||||
   .. automethod:: ormqr
 | 
			
		||||
   .. automethod:: permute
 | 
			
		||||
   .. automethod:: pin_memory
 | 
			
		||||
   .. automethod:: potrf
 | 
			
		||||
   .. automethod:: potri
 | 
			
		||||
   .. automethod:: potrs
 | 
			
		||||
   .. automethod:: pow
 | 
			
		||||
   .. automethod:: pow_
 | 
			
		||||
   .. automethod:: prod
 | 
			
		||||
   .. automethod:: pstrf
 | 
			
		||||
   .. automethod:: qr
 | 
			
		||||
   .. automethod:: random_
 | 
			
		||||
   .. automethod:: reciprocal
 | 
			
		||||
   .. automethod:: reciprocal_
 | 
			
		||||
   .. automethod:: remainder
 | 
			
		||||
   .. automethod:: remainder_
 | 
			
		||||
   .. automethod:: renorm
 | 
			
		||||
   .. automethod:: renorm_
 | 
			
		||||
   .. automethod:: repeat
 | 
			
		||||
   .. automethod:: resize_
 | 
			
		||||
   .. automethod:: resize_as_
 | 
			
		||||
   .. automethod:: round
 | 
			
		||||
   .. automethod:: round_
 | 
			
		||||
   .. automethod:: rsqrt
 | 
			
		||||
   .. automethod:: rsqrt_
 | 
			
		||||
   .. automethod:: scatter_
 | 
			
		||||
   .. automethod:: select
 | 
			
		||||
   .. automethod:: set_
 | 
			
		||||
   .. automethod:: share_memory_
 | 
			
		||||
   .. automethod:: short
 | 
			
		||||
   .. automethod:: sigmoid
 | 
			
		||||
   .. automethod:: sigmoid_
 | 
			
		||||
   .. automethod:: sign
 | 
			
		||||
   .. automethod:: sign_
 | 
			
		||||
   .. automethod:: sin
 | 
			
		||||
   .. automethod:: sin_
 | 
			
		||||
   .. automethod:: sinh
 | 
			
		||||
   .. automethod:: sinh_
 | 
			
		||||
   .. automethod:: size
 | 
			
		||||
   .. automethod:: sort
 | 
			
		||||
   .. automethod:: split
 | 
			
		||||
   .. automethod:: sqrt
 | 
			
		||||
   .. automethod:: sqrt_
 | 
			
		||||
   .. automethod:: squeeze
 | 
			
		||||
   .. automethod:: squeeze_
 | 
			
		||||
   .. automethod:: std
 | 
			
		||||
   .. automethod:: storage
 | 
			
		||||
   .. automethod:: storage_offset
 | 
			
		||||
   .. automethod:: storage_type
 | 
			
		||||
   .. automethod:: stride
 | 
			
		||||
   .. automethod:: sub
 | 
			
		||||
   .. automethod:: sub_
 | 
			
		||||
   .. automethod:: sum
 | 
			
		||||
   .. automethod:: svd
 | 
			
		||||
   .. automethod:: symeig
 | 
			
		||||
   .. automethod:: t
 | 
			
		||||
   .. automethod:: t_
 | 
			
		||||
   .. automethod:: tan
 | 
			
		||||
   .. automethod:: tan_
 | 
			
		||||
   .. automethod:: tanh
 | 
			
		||||
   .. automethod:: tanh_
 | 
			
		||||
   .. automethod:: tolist
 | 
			
		||||
   .. automethod:: topk
 | 
			
		||||
   .. automethod:: trace
 | 
			
		||||
   .. automethod:: transpose
 | 
			
		||||
   .. automethod:: transpose_
 | 
			
		||||
   .. automethod:: tril
 | 
			
		||||
   .. automethod:: tril_
 | 
			
		||||
   .. automethod:: triu
 | 
			
		||||
   .. automethod:: triu_
 | 
			
		||||
   .. automethod:: trtrs
 | 
			
		||||
   .. automethod:: trunc
 | 
			
		||||
   .. automethod:: trunc_
 | 
			
		||||
   .. automethod:: type
 | 
			
		||||
   .. automethod:: type_as
 | 
			
		||||
   .. automethod:: unfold
 | 
			
		||||
   .. automethod:: uniform_
 | 
			
		||||
   .. automethod:: unsqueeze
 | 
			
		||||
   .. automethod:: unsqueeze_
 | 
			
		||||
   .. automethod:: var
 | 
			
		||||
   .. automethod:: view
 | 
			
		||||
   .. automethod:: view_as
 | 
			
		||||
   .. automethod:: zero_
 | 
			
		||||
							
								
								
									
										186
									
								
								docs/source/torch.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								docs/source/torch.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,186 @@
 | 
			
		||||
torch
 | 
			
		||||
===================================
 | 
			
		||||
.. automodule:: torch
 | 
			
		||||
 | 
			
		||||
Tensors
 | 
			
		||||
----------------------------------
 | 
			
		||||
.. autofunction:: is_tensor
 | 
			
		||||
.. autofunction:: is_storage
 | 
			
		||||
.. autofunction:: set_default_tensor_type
 | 
			
		||||
.. autofunction:: numel
 | 
			
		||||
.. autofunction:: set_printoptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Creation Ops
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
.. autofunction:: eye
 | 
			
		||||
.. autofunction:: from_numpy
 | 
			
		||||
.. autofunction:: linspace
 | 
			
		||||
.. autofunction:: logspace
 | 
			
		||||
.. autofunction:: ones
 | 
			
		||||
.. autofunction:: rand
 | 
			
		||||
.. autofunction:: randn
 | 
			
		||||
.. autofunction:: randperm
 | 
			
		||||
.. autofunction:: arange
 | 
			
		||||
.. autofunction:: range
 | 
			
		||||
.. autofunction:: zeros
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Indexing, Slicing, Joining, Mutating Ops
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
.. autofunction:: cat
 | 
			
		||||
.. autofunction:: chunk
 | 
			
		||||
.. autofunction:: gather
 | 
			
		||||
.. autofunction:: index_select
 | 
			
		||||
.. autofunction:: masked_select
 | 
			
		||||
.. autofunction:: nonzero
 | 
			
		||||
.. autofunction:: split
 | 
			
		||||
.. autofunction:: squeeze
 | 
			
		||||
.. autofunction:: stack
 | 
			
		||||
.. autofunction:: t
 | 
			
		||||
.. autofunction:: transpose
 | 
			
		||||
.. autofunction:: unbind
 | 
			
		||||
.. autofunction:: unsqueeze
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Random sampling
 | 
			
		||||
----------------------------------
 | 
			
		||||
.. autofunction:: manual_seed
 | 
			
		||||
.. autofunction:: initial_seed
 | 
			
		||||
.. autofunction:: get_rng_state
 | 
			
		||||
.. autofunction:: set_rng_state
 | 
			
		||||
.. autodata:: default_generator
 | 
			
		||||
.. autofunction:: bernoulli
 | 
			
		||||
.. autofunction:: multinomial
 | 
			
		||||
.. autofunction:: normal
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Serialization
 | 
			
		||||
----------------------------------
 | 
			
		||||
.. autofunction:: save
 | 
			
		||||
.. autofunction:: load
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Parallelism
 | 
			
		||||
----------------------------------
 | 
			
		||||
.. autofunction:: get_num_threads
 | 
			
		||||
.. autofunction:: set_num_threads
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Math operations
 | 
			
		||||
----------------------------------
 | 
			
		||||
 | 
			
		||||
Pointwise Ops
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
.. autofunction:: abs
 | 
			
		||||
.. autofunction:: acos
 | 
			
		||||
.. autofunction:: add
 | 
			
		||||
.. autofunction:: addcdiv
 | 
			
		||||
.. autofunction:: addcmul
 | 
			
		||||
.. autofunction:: asin
 | 
			
		||||
.. autofunction:: atan
 | 
			
		||||
.. autofunction:: atan2
 | 
			
		||||
.. autofunction:: ceil
 | 
			
		||||
.. autofunction:: clamp
 | 
			
		||||
.. autofunction:: cos
 | 
			
		||||
.. autofunction:: cosh
 | 
			
		||||
.. autofunction:: div
 | 
			
		||||
.. autofunction:: exp
 | 
			
		||||
.. autofunction:: floor
 | 
			
		||||
.. autofunction:: fmod
 | 
			
		||||
.. autofunction:: frac
 | 
			
		||||
.. autofunction:: lerp
 | 
			
		||||
.. autofunction:: log
 | 
			
		||||
.. autofunction:: log1p
 | 
			
		||||
.. autofunction:: mul
 | 
			
		||||
.. autofunction:: neg
 | 
			
		||||
.. autofunction:: pow
 | 
			
		||||
.. autofunction:: reciprocal
 | 
			
		||||
.. autofunction:: remainder
 | 
			
		||||
.. autofunction:: round
 | 
			
		||||
.. autofunction:: rsqrt
 | 
			
		||||
.. autofunction:: sigmoid
 | 
			
		||||
.. autofunction:: sign
 | 
			
		||||
.. autofunction:: sin
 | 
			
		||||
.. autofunction:: sinh
 | 
			
		||||
.. autofunction:: sqrt
 | 
			
		||||
.. autofunction:: tan
 | 
			
		||||
.. autofunction:: tanh
 | 
			
		||||
.. autofunction:: trunc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Reduction Ops
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
.. autofunction:: cumprod
 | 
			
		||||
.. autofunction:: cumsum
 | 
			
		||||
.. autofunction:: dist
 | 
			
		||||
.. autofunction:: mean
 | 
			
		||||
.. autofunction:: median
 | 
			
		||||
.. autofunction:: mode
 | 
			
		||||
.. autofunction:: norm
 | 
			
		||||
.. autofunction:: prod
 | 
			
		||||
.. autofunction:: std
 | 
			
		||||
.. autofunction:: sum
 | 
			
		||||
.. autofunction:: var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Comparison Ops
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
.. autofunction:: eq
 | 
			
		||||
.. autofunction:: equal
 | 
			
		||||
.. autofunction:: ge
 | 
			
		||||
.. autofunction:: gt
 | 
			
		||||
.. autofunction:: kthvalue
 | 
			
		||||
.. autofunction:: le
 | 
			
		||||
.. autofunction:: lt
 | 
			
		||||
.. autofunction:: max
 | 
			
		||||
.. autofunction:: min
 | 
			
		||||
.. autofunction:: ne
 | 
			
		||||
.. autofunction:: sort
 | 
			
		||||
.. autofunction:: topk
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Other Operations
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
.. autofunction:: cross
 | 
			
		||||
.. autofunction:: diag
 | 
			
		||||
.. autofunction:: histc
 | 
			
		||||
.. autofunction:: renorm
 | 
			
		||||
.. autofunction:: trace
 | 
			
		||||
.. autofunction:: tril
 | 
			
		||||
.. autofunction:: triu
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BLAS and LAPACK Operations
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
.. autofunction:: addbmm
 | 
			
		||||
.. autofunction:: addmm
 | 
			
		||||
.. autofunction:: addmv
 | 
			
		||||
.. autofunction:: addr
 | 
			
		||||
.. autofunction:: baddbmm
 | 
			
		||||
.. autofunction:: bmm
 | 
			
		||||
.. autofunction:: btrifact
 | 
			
		||||
.. autofunction:: btrisolve
 | 
			
		||||
.. autofunction:: dot
 | 
			
		||||
.. autofunction:: eig
 | 
			
		||||
.. autofunction:: gels
 | 
			
		||||
.. autofunction:: geqrf
 | 
			
		||||
.. autofunction:: ger
 | 
			
		||||
.. autofunction:: gesv
 | 
			
		||||
.. autofunction:: inverse
 | 
			
		||||
.. autofunction:: matmul
 | 
			
		||||
.. autofunction:: mm
 | 
			
		||||
.. autofunction:: mv
 | 
			
		||||
.. autofunction:: orgqr
 | 
			
		||||
.. autofunction:: ormqr
 | 
			
		||||
.. autofunction:: potrf
 | 
			
		||||
.. autofunction:: potri
 | 
			
		||||
.. autofunction:: potrs
 | 
			
		||||
.. autofunction:: pstrf
 | 
			
		||||
.. autofunction:: qr
 | 
			
		||||
.. autofunction:: svd
 | 
			
		||||
.. autofunction:: symeig
 | 
			
		||||
.. autofunction:: trtrs
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										112
									
								
								docs/source/torchvision/datasets.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								docs/source/torchvision/datasets.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,112 @@
 | 
			
		||||
torchvision.datasets
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
All datasets are subclasses of :class:`torch.utils.data.Dataset`
 | 
			
		||||
i.e, they have ``__getitem__`` and ``__len__`` methods implemented.
 | 
			
		||||
Hence, they can all be passed to a :class:`torch.utils.data.DataLoader`
 | 
			
		||||
which can load multiple samples parallelly using ``torch.multiprocessing`` workers. 
 | 
			
		||||
For example: ::
 | 
			
		||||
    
 | 
			
		||||
    imagenet_data = torchvision.datasets.ImageFolder('path/to/imagenet_root/')
 | 
			
		||||
    data_loader = torch.utils.data.DataLoader(imagenet_data, 
 | 
			
		||||
                                              batch_size=4,
 | 
			
		||||
                                              shuffle=True,
 | 
			
		||||
                                              num_workers=args.nThreads)
 | 
			
		||||
 | 
			
		||||
The following datasets are available:
 | 
			
		||||
 | 
			
		||||
.. contents:: Datasets
 | 
			
		||||
    :local:
 | 
			
		||||
 | 
			
		||||
All the datasets have almost similar API. They all have two common arguments:
 | 
			
		||||
``transform`` and  ``target_transform`` to transform the input and target respectively.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torchvision.datasets 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MNIST
 | 
			
		||||
~~~~~
 | 
			
		||||
 | 
			
		||||
.. autoclass:: MNIST
 | 
			
		||||
 | 
			
		||||
COCO
 | 
			
		||||
~~~~
 | 
			
		||||
 | 
			
		||||
.. note ::
 | 
			
		||||
    These require the `COCO API to be installed`_
 | 
			
		||||
 | 
			
		||||
.. _COCO API to be installed: https://github.com/pdollar/coco/tree/master/PythonAPI
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Captions
 | 
			
		||||
^^^^^^^^
 | 
			
		||||
 | 
			
		||||
.. autoclass:: CocoCaptions
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Detection
 | 
			
		||||
^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
.. autoclass:: CocoDetection
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
LSUN
 | 
			
		||||
~~~~
 | 
			
		||||
 | 
			
		||||
.. autoclass:: LSUN
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
ImageFolder
 | 
			
		||||
~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
.. autoclass:: ImageFolder
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Imagenet-12
 | 
			
		||||
~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
This should simply be implemented with an ``ImageFolder`` dataset.
 | 
			
		||||
The data is preprocessed `as described
 | 
			
		||||
here <https://github.com/facebook/fb.resnet.torch/blob/master/INSTALL.md#download-the-imagenet-dataset>`__
 | 
			
		||||
 | 
			
		||||
`Here is an
 | 
			
		||||
example <https://github.com/pytorch/examples/blob/27e2a46c1d1505324032b1d94fc6ce24d5b67e97/imagenet/main.py#L48-L62>`__.
 | 
			
		||||
 | 
			
		||||
CIFAR
 | 
			
		||||
~~~~~
 | 
			
		||||
 | 
			
		||||
.. autoclass:: CIFAR10
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
STL10
 | 
			
		||||
~~~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. autoclass:: STL10
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
SVHN
 | 
			
		||||
~~~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. autoclass:: SVHN
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
PhotoTour
 | 
			
		||||
~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. autoclass:: PhotoTour
 | 
			
		||||
  :members: __getitem__
 | 
			
		||||
  :special-members:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								docs/source/torchvision/models.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								docs/source/torchvision/models.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
torchvision.models
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torchvision.models
 | 
			
		||||
 | 
			
		||||
.. automodule:: torchvision.models
 | 
			
		||||
   :members: alexnet, resnet18, resnet34, resnet50, resnet101, resnet152,
 | 
			
		||||
             vgg11, vgg11_bn, vgg13, vgg13_bn, vgg16, vgg16_bn, vgg19,
 | 
			
		||||
             vgg19_bn, inception_v3, squeezenet1_0, squeezenet1_1, densenet121, 
 | 
			
		||||
             densenet169, densenet201, densenet161
 | 
			
		||||
   :undoc-members:
 | 
			
		||||
							
								
								
									
										8
									
								
								docs/source/torchvision/torchvision.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								docs/source/torchvision/torchvision.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
torchvision
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
The :mod:`torchvision` package consists of popular datasets, model
 | 
			
		||||
architectures, and common image transformations for computer vision.
 | 
			
		||||
 | 
			
		||||
.. automodule:: torchvision
 | 
			
		||||
   :members:
 | 
			
		||||
							
								
								
									
										48
									
								
								docs/source/torchvision/transforms.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								docs/source/torchvision/transforms.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
torchvision.transforms
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torchvision.transforms
 | 
			
		||||
 | 
			
		||||
Transforms are common image transforms. They can be chained together using :class:`Compose`
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Compose
 | 
			
		||||
 | 
			
		||||
Transforms on PIL.Image
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Scale
 | 
			
		||||
 | 
			
		||||
.. autoclass:: CenterCrop
 | 
			
		||||
 | 
			
		||||
.. autoclass:: RandomCrop
 | 
			
		||||
 | 
			
		||||
.. autoclass:: RandomHorizontalFlip
 | 
			
		||||
 | 
			
		||||
.. autoclass:: RandomSizedCrop
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Pad
 | 
			
		||||
 | 
			
		||||
Transforms on torch.\*Tensor
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Normalize
 | 
			
		||||
	:members: __call__
 | 
			
		||||
	:special-members:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Conversion Transforms
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
.. autoclass:: ToTensor
 | 
			
		||||
	:members: __call__
 | 
			
		||||
	:special-members:
 | 
			
		||||
 | 
			
		||||
.. autoclass:: ToPILImage
 | 
			
		||||
	:members: __call__
 | 
			
		||||
	:special-members:
 | 
			
		||||
 | 
			
		||||
Generic Transforms
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
.. autoclass:: Lambda
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								docs/source/torchvision/utils.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								docs/source/torchvision/utils.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
torchvision.utils
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: torchvision.utils
 | 
			
		||||
 | 
			
		||||
.. autofunction:: make_grid
 | 
			
		||||
 | 
			
		||||
.. autofunction:: save_image
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
pyyaml
 | 
			
		||||
							
								
								
									
										499
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										499
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,499 @@
 | 
			
		||||
from setuptools import setup, Extension, distutils, Command, find_packages
 | 
			
		||||
import setuptools.command.build_ext
 | 
			
		||||
import setuptools.command.install
 | 
			
		||||
import setuptools.command.develop
 | 
			
		||||
import setuptools.command.build_py
 | 
			
		||||
import distutils.unixccompiler
 | 
			
		||||
import distutils.command.build
 | 
			
		||||
import distutils.command.clean
 | 
			
		||||
import platform
 | 
			
		||||
import subprocess
 | 
			
		||||
import shutil
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from tools.setup_helpers.env import check_env_flag
 | 
			
		||||
from tools.setup_helpers.cuda import WITH_CUDA, CUDA_HOME
 | 
			
		||||
from tools.setup_helpers.cudnn import WITH_CUDNN, CUDNN_LIB_DIR, CUDNN_INCLUDE_DIR
 | 
			
		||||
from tools.setup_helpers.split_types import split_types
 | 
			
		||||
DEBUG = check_env_flag('DEBUG')
 | 
			
		||||
WITH_DISTRIBUTED = not check_env_flag('NO_DISTRIBUTED')
 | 
			
		||||
WITH_DISTRIBUTED_MW = WITH_DISTRIBUTED and check_env_flag('WITH_DISTRIBUTED_MW')
 | 
			
		||||
WITH_NCCL = WITH_CUDA and platform.system() != 'Darwin'
 | 
			
		||||
SYSTEM_NCCL = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Workaround setuptools -Wstrict-prototypes warnings
 | 
			
		||||
# I lifted this code from https://stackoverflow.com/a/29634231/23845
 | 
			
		||||
################################################################################
 | 
			
		||||
import distutils.sysconfig
 | 
			
		||||
cfg_vars = distutils.sysconfig.get_config_vars()
 | 
			
		||||
for key, value in cfg_vars.items():
 | 
			
		||||
    if type(value) == str:
 | 
			
		||||
            cfg_vars[key] = value.replace("-Wstrict-prototypes", "")
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Monkey-patch setuptools to compile in parallel
 | 
			
		||||
################################################################################
 | 
			
		||||
original_link = distutils.unixccompiler.UnixCCompiler.link
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parallelCCompile(self, sources, output_dir=None, macros=None,
 | 
			
		||||
                     include_dirs=None, debug=0, extra_preargs=None,
 | 
			
		||||
                     extra_postargs=None, depends=None):
 | 
			
		||||
    # those lines are copied from distutils.ccompiler.CCompiler directly
 | 
			
		||||
    macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
 | 
			
		||||
        output_dir, macros, include_dirs, sources, depends, extra_postargs)
 | 
			
		||||
    cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
 | 
			
		||||
 | 
			
		||||
    # compile using a thread pool
 | 
			
		||||
    import multiprocessing.pool
 | 
			
		||||
 | 
			
		||||
    def _single_compile(obj):
 | 
			
		||||
        src, ext = build[obj]
 | 
			
		||||
        self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
 | 
			
		||||
    num_jobs = multiprocessing.cpu_count()
 | 
			
		||||
    multiprocessing.pool.ThreadPool(num_jobs).map(_single_compile, objects)
 | 
			
		||||
 | 
			
		||||
    return objects
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def patched_link(self, *args, **kwargs):
 | 
			
		||||
    _cxx = self.compiler_cxx
 | 
			
		||||
    self.compiler_cxx = None
 | 
			
		||||
    result = original_link(self, *args, **kwargs)
 | 
			
		||||
    self.compiler_cxx = _cxx
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
distutils.ccompiler.CCompiler.compile = parallelCCompile
 | 
			
		||||
distutils.unixccompiler.UnixCCompiler.link = patched_link
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Custom build commands
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class build_deps(Command):
 | 
			
		||||
    user_options = []
 | 
			
		||||
 | 
			
		||||
    def initialize_options(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def finalize_options(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        from tools.nnwrap import generate_wrappers as generate_nn_wrappers
 | 
			
		||||
        build_all_cmd = ['bash', 'torch/lib/build_all.sh']
 | 
			
		||||
        if WITH_CUDA:
 | 
			
		||||
            build_all_cmd += ['--with-cuda']
 | 
			
		||||
        if WITH_NCCL and not SYSTEM_NCCL:
 | 
			
		||||
            build_all_cmd += ['--with-nccl']
 | 
			
		||||
        if WITH_DISTRIBUTED:
 | 
			
		||||
            build_all_cmd += ['--with-distributed']
 | 
			
		||||
        if subprocess.call(build_all_cmd) != 0:
 | 
			
		||||
            sys.exit(1)
 | 
			
		||||
        generate_nn_wrappers()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class build_module(Command):
 | 
			
		||||
    user_options = []
 | 
			
		||||
 | 
			
		||||
    def initialize_options(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def finalize_options(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        self.run_command('build_py')
 | 
			
		||||
        self.run_command('build_ext')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class build_py(setuptools.command.build_py.build_py):
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        self.create_version_file()
 | 
			
		||||
        setuptools.command.build_py.build_py.run(self)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def create_version_file():
 | 
			
		||||
        global version, cwd
 | 
			
		||||
        print('-- Building version ' + version)
 | 
			
		||||
        version_path = os.path.join(cwd, 'torch', 'version.py')
 | 
			
		||||
        with open(version_path, 'w') as f:
 | 
			
		||||
            f.write("__version__ = '{}'\n".format(version))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class develop(setuptools.command.develop.develop):
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        build_py.create_version_file()
 | 
			
		||||
        setuptools.command.develop.develop.run(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class build_ext(setuptools.command.build_ext.build_ext):
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        # Print build options
 | 
			
		||||
        if WITH_NUMPY:
 | 
			
		||||
            print('-- Building with NumPy bindings')
 | 
			
		||||
        else:
 | 
			
		||||
            print('-- NumPy not found')
 | 
			
		||||
        if WITH_CUDNN:
 | 
			
		||||
            print('-- Detected cuDNN at ' + CUDNN_LIB_DIR + ', ' + CUDNN_INCLUDE_DIR)
 | 
			
		||||
        else:
 | 
			
		||||
            print('-- Not using cuDNN')
 | 
			
		||||
        if WITH_CUDA:
 | 
			
		||||
            print('-- Detected CUDA at ' + CUDA_HOME)
 | 
			
		||||
        else:
 | 
			
		||||
            print('-- Not using CUDA')
 | 
			
		||||
        if WITH_NCCL and SYSTEM_NCCL:
 | 
			
		||||
            print('-- Using system provided NCCL library')
 | 
			
		||||
        elif WITH_NCCL:
 | 
			
		||||
            print('-- Building NCCL library')
 | 
			
		||||
        else:
 | 
			
		||||
            print('-- Not using NCCL')
 | 
			
		||||
        if WITH_DISTRIBUTED:
 | 
			
		||||
            print('-- Building with distributed package ')
 | 
			
		||||
        else:
 | 
			
		||||
            print('-- Building without distributed package')
 | 
			
		||||
 | 
			
		||||
        # cwrap depends on pyyaml, so we can't import it earlier
 | 
			
		||||
        from tools.cwrap import cwrap
 | 
			
		||||
        from tools.cwrap.plugins.THPPlugin import THPPlugin
 | 
			
		||||
        from tools.cwrap.plugins.ArgcountSortPlugin import ArgcountSortPlugin
 | 
			
		||||
        from tools.cwrap.plugins.AutoGPU import AutoGPU
 | 
			
		||||
        from tools.cwrap.plugins.BoolOption import BoolOption
 | 
			
		||||
        from tools.cwrap.plugins.KwargsPlugin import KwargsPlugin
 | 
			
		||||
        from tools.cwrap.plugins.NullableArguments import NullableArguments
 | 
			
		||||
        from tools.cwrap.plugins.CuDNNPlugin import CuDNNPlugin
 | 
			
		||||
        from tools.cwrap.plugins.WrapDim import WrapDim
 | 
			
		||||
        from tools.cwrap.plugins.AssertNDim import AssertNDim
 | 
			
		||||
        from tools.cwrap.plugins.Broadcast import Broadcast
 | 
			
		||||
        from tools.cwrap.plugins.ProcessorSpecificPlugin import ProcessorSpecificPlugin
 | 
			
		||||
        thp_plugin = THPPlugin()
 | 
			
		||||
        cwrap('torch/csrc/generic/TensorMethods.cwrap', plugins=[
 | 
			
		||||
            ProcessorSpecificPlugin(), BoolOption(), thp_plugin,
 | 
			
		||||
            AutoGPU(condition='IS_CUDA'), ArgcountSortPlugin(), KwargsPlugin(),
 | 
			
		||||
            AssertNDim(), WrapDim(), Broadcast()
 | 
			
		||||
        ])
 | 
			
		||||
        cwrap('torch/csrc/cudnn/cuDNN.cwrap', plugins=[
 | 
			
		||||
            CuDNNPlugin(), NullableArguments()
 | 
			
		||||
        ])
 | 
			
		||||
        # It's an old-style class in Python 2.7...
 | 
			
		||||
        setuptools.command.build_ext.build_ext.run(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class build(distutils.command.build.build):
 | 
			
		||||
    sub_commands = [
 | 
			
		||||
        ('build_deps', lambda self: True),
 | 
			
		||||
    ] + distutils.command.build.build.sub_commands
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class install(setuptools.command.install.install):
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        if not self.skip_build:
 | 
			
		||||
            self.run_command('build_deps')
 | 
			
		||||
        setuptools.command.install.install.run(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class clean(distutils.command.clean.clean):
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        import glob
 | 
			
		||||
        with open('.gitignore', 'r') as f:
 | 
			
		||||
            ignores = f.read()
 | 
			
		||||
            for wildcard in filter(bool, ignores.split('\n')):
 | 
			
		||||
                for filename in glob.glob(wildcard):
 | 
			
		||||
                    try:
 | 
			
		||||
                        os.remove(filename)
 | 
			
		||||
                    except OSError:
 | 
			
		||||
                        shutil.rmtree(filename, ignore_errors=True)
 | 
			
		||||
 | 
			
		||||
        # It's an old-style class in Python 2.7...
 | 
			
		||||
        distutils.command.clean.clean.run(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Configure compile flags
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
include_dirs = []
 | 
			
		||||
library_dirs = []
 | 
			
		||||
extra_link_args = []
 | 
			
		||||
extra_compile_args = ['-std=c++11', '-Wno-write-strings',
 | 
			
		||||
                      # Python 2.6 requires -fno-strict-aliasing, see
 | 
			
		||||
                      # http://legacy.python.org/dev/peps/pep-3123/
 | 
			
		||||
                      '-fno-strict-aliasing']
 | 
			
		||||
 | 
			
		||||
cwd = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
lib_path = os.path.join(cwd, "torch", "lib")
 | 
			
		||||
 | 
			
		||||
tmp_install_path = lib_path + "/tmp_install"
 | 
			
		||||
include_dirs += [
 | 
			
		||||
    cwd,
 | 
			
		||||
    os.path.join(cwd, "torch", "csrc"),
 | 
			
		||||
    tmp_install_path + "/include",
 | 
			
		||||
    tmp_install_path + "/include/TH",
 | 
			
		||||
    tmp_install_path + "/include/THPP",
 | 
			
		||||
    tmp_install_path + "/include/THNN",
 | 
			
		||||
    tmp_install_path + "/include/ATen",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
library_dirs.append(lib_path)
 | 
			
		||||
 | 
			
		||||
# we specify exact lib names to avoid conflict with lua-torch installs
 | 
			
		||||
TH_LIB = os.path.join(lib_path, 'libTH.so.1')
 | 
			
		||||
THS_LIB = os.path.join(lib_path, 'libTHS.so.1')
 | 
			
		||||
THC_LIB = os.path.join(lib_path, 'libTHC.so.1')
 | 
			
		||||
THCS_LIB = os.path.join(lib_path, 'libTHCS.so.1')
 | 
			
		||||
THNN_LIB = os.path.join(lib_path, 'libTHNN.so.1')
 | 
			
		||||
THCUNN_LIB = os.path.join(lib_path, 'libTHCUNN.so.1')
 | 
			
		||||
THPP_LIB = os.path.join(lib_path, 'libTHPP.so.1')
 | 
			
		||||
ATEN_LIB = os.path.join(lib_path, 'libATen.so.1')
 | 
			
		||||
GLOO_LIB = os.path.join(lib_path, 'libgloo.a')
 | 
			
		||||
GLOO_CUDA_LIB = os.path.join(lib_path, 'libgloo_cuda.a')
 | 
			
		||||
THD_LIB = os.path.join(lib_path, 'libTHD.a')
 | 
			
		||||
NCCL_LIB = os.path.join(lib_path, 'libnccl.so.1')
 | 
			
		||||
if platform.system() == 'Darwin':
 | 
			
		||||
    TH_LIB = os.path.join(lib_path, 'libTH.1.dylib')
 | 
			
		||||
    THS_LIB = os.path.join(lib_path, 'libTHS.1.dylib')
 | 
			
		||||
    THC_LIB = os.path.join(lib_path, 'libTHC.1.dylib')
 | 
			
		||||
    THCS_LIB = os.path.join(lib_path, 'libTHCS.1.dylib')
 | 
			
		||||
    THNN_LIB = os.path.join(lib_path, 'libTHNN.1.dylib')
 | 
			
		||||
    THCUNN_LIB = os.path.join(lib_path, 'libTHCUNN.1.dylib')
 | 
			
		||||
    THPP_LIB = os.path.join(lib_path, 'libTHPP.1.dylib')
 | 
			
		||||
    ATEN_LIB = os.path.join(lib_path, 'libATen.1.dylib')
 | 
			
		||||
    NCCL_LIB = os.path.join(lib_path, 'libnccl.1.dylib')
 | 
			
		||||
 | 
			
		||||
if WITH_NCCL and subprocess.call('ldconfig -p | grep libnccl >/dev/null', shell=True) == 0:
 | 
			
		||||
    SYSTEM_NCCL = True
 | 
			
		||||
 | 
			
		||||
main_compile_args = ['-D_THP_CORE']
 | 
			
		||||
main_libraries = ['shm']
 | 
			
		||||
main_link_args = [TH_LIB, THS_LIB, THPP_LIB, THNN_LIB, ATEN_LIB]
 | 
			
		||||
main_sources = [
 | 
			
		||||
    "torch/csrc/PtrWrapper.cpp",
 | 
			
		||||
    "torch/csrc/Module.cpp",
 | 
			
		||||
    "torch/csrc/Generator.cpp",
 | 
			
		||||
    "torch/csrc/Size.cpp",
 | 
			
		||||
    "torch/csrc/Exceptions.cpp",
 | 
			
		||||
    "torch/csrc/Storage.cpp",
 | 
			
		||||
    "torch/csrc/DynamicTypes.cpp",
 | 
			
		||||
    "torch/csrc/byte_order.cpp",
 | 
			
		||||
    "torch/csrc/utils.cpp",
 | 
			
		||||
    "torch/csrc/expand_utils.cpp",
 | 
			
		||||
    "torch/csrc/utils/object_ptr.cpp",
 | 
			
		||||
    "torch/csrc/utils/tuple_parser.cpp",
 | 
			
		||||
    "torch/csrc/allocators.cpp",
 | 
			
		||||
    "torch/csrc/serialization.cpp",
 | 
			
		||||
    "torch/csrc/autograd/init.cpp",
 | 
			
		||||
    "torch/csrc/autograd/engine.cpp",
 | 
			
		||||
    "torch/csrc/autograd/function.cpp",
 | 
			
		||||
    "torch/csrc/autograd/variable.cpp",
 | 
			
		||||
    "torch/csrc/autograd/input_buffer.cpp",
 | 
			
		||||
    "torch/csrc/autograd/python_function.cpp",
 | 
			
		||||
    "torch/csrc/autograd/python_cpp_function.cpp",
 | 
			
		||||
    "torch/csrc/autograd/python_variable.cpp",
 | 
			
		||||
    "torch/csrc/autograd/python_engine.cpp",
 | 
			
		||||
    "torch/csrc/autograd/python_hook.cpp",
 | 
			
		||||
    "torch/csrc/autograd/functions/batch_normalization.cpp",
 | 
			
		||||
    "torch/csrc/autograd/functions/convolution.cpp",
 | 
			
		||||
    "torch/csrc/autograd/functions/basic_ops.cpp",
 | 
			
		||||
    "torch/csrc/autograd/functions/tensor.cpp",
 | 
			
		||||
    "torch/csrc/autograd/functions/accumulate_grad.cpp",
 | 
			
		||||
    "torch/csrc/autograd/functions/utils.cpp",
 | 
			
		||||
    "torch/csrc/autograd/functions/init.cpp",
 | 
			
		||||
    "torch/csrc/nn/THNN_generic.cpp",
 | 
			
		||||
]
 | 
			
		||||
main_sources += split_types("torch/csrc/Tensor.cpp")
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import numpy as np
 | 
			
		||||
    include_dirs += [np.get_include()]
 | 
			
		||||
    extra_compile_args += ['-DWITH_NUMPY']
 | 
			
		||||
    WITH_NUMPY = True
 | 
			
		||||
except ImportError:
 | 
			
		||||
    WITH_NUMPY = False
 | 
			
		||||
 | 
			
		||||
if WITH_DISTRIBUTED:
 | 
			
		||||
    extra_compile_args += ['-DWITH_DISTRIBUTED']
 | 
			
		||||
    main_sources += [
 | 
			
		||||
        "torch/csrc/distributed/Module.cpp",
 | 
			
		||||
        "torch/csrc/distributed/utils.cpp",
 | 
			
		||||
    ]
 | 
			
		||||
    if WITH_DISTRIBUTED_MW:
 | 
			
		||||
        main_sources += [
 | 
			
		||||
            "torch/csrc/distributed/Tensor.cpp",
 | 
			
		||||
            "torch/csrc/distributed/Storage.cpp",
 | 
			
		||||
        ]
 | 
			
		||||
        extra_compile_args += ['-DWITH_DISTRIBUTED_MW']
 | 
			
		||||
    include_dirs += [tmp_install_path + "/include/THD"]
 | 
			
		||||
    main_link_args += [THD_LIB]
 | 
			
		||||
    if platform.system() == 'Linux':
 | 
			
		||||
        main_link_args += [GLOO_LIB]
 | 
			
		||||
 | 
			
		||||
if WITH_CUDA:
 | 
			
		||||
    cuda_lib_dirs = ['lib64', 'lib']
 | 
			
		||||
    cuda_include_path = os.path.join(CUDA_HOME, 'include')
 | 
			
		||||
    for lib_dir in cuda_lib_dirs:
 | 
			
		||||
        cuda_lib_path = os.path.join(CUDA_HOME, lib_dir)
 | 
			
		||||
        if os.path.exists(cuda_lib_path):
 | 
			
		||||
            break
 | 
			
		||||
    include_dirs.append(cuda_include_path)
 | 
			
		||||
    include_dirs.append(tmp_install_path + "/include/THCUNN")
 | 
			
		||||
    library_dirs.append(cuda_lib_path)
 | 
			
		||||
    extra_link_args.append('-Wl,-rpath,' + cuda_lib_path)
 | 
			
		||||
    extra_compile_args += ['-DWITH_CUDA']
 | 
			
		||||
    extra_compile_args += ['-DCUDA_LIB_PATH=' + cuda_lib_path]
 | 
			
		||||
    main_libraries += ['cudart', 'nvToolsExt']
 | 
			
		||||
    main_link_args += [THC_LIB, THCS_LIB, THCUNN_LIB]
 | 
			
		||||
    if platform.system() == 'Linux':
 | 
			
		||||
        main_link_args += [GLOO_CUDA_LIB]
 | 
			
		||||
    main_sources += [
 | 
			
		||||
        "torch/csrc/cuda/Module.cpp",
 | 
			
		||||
        "torch/csrc/cuda/Storage.cpp",
 | 
			
		||||
        "torch/csrc/cuda/Stream.cpp",
 | 
			
		||||
        "torch/csrc/cuda/AutoGPU.cpp",
 | 
			
		||||
        "torch/csrc/cuda/utils.cpp",
 | 
			
		||||
        "torch/csrc/cuda/expand_utils.cpp",
 | 
			
		||||
        "torch/csrc/cuda/serialization.cpp",
 | 
			
		||||
    ]
 | 
			
		||||
    main_sources += split_types("torch/csrc/cuda/Tensor.cpp")
 | 
			
		||||
 | 
			
		||||
if WITH_NCCL:
 | 
			
		||||
    if SYSTEM_NCCL:
 | 
			
		||||
        main_libraries += ['nccl']
 | 
			
		||||
    else:
 | 
			
		||||
        main_link_args += [NCCL_LIB]
 | 
			
		||||
    extra_compile_args += ['-DWITH_NCCL']
 | 
			
		||||
 | 
			
		||||
if WITH_CUDNN:
 | 
			
		||||
    main_libraries += ['cudnn']
 | 
			
		||||
    include_dirs.append(CUDNN_INCLUDE_DIR)
 | 
			
		||||
    library_dirs.append(CUDNN_LIB_DIR)
 | 
			
		||||
    main_sources += [
 | 
			
		||||
        "torch/csrc/cudnn/BatchNorm.cpp",
 | 
			
		||||
        "torch/csrc/cudnn/Conv.cpp",
 | 
			
		||||
        "torch/csrc/cudnn/cuDNN.cpp",
 | 
			
		||||
        "torch/csrc/cudnn/GridSampler.cpp",
 | 
			
		||||
        "torch/csrc/cudnn/AffineGridGenerator.cpp",
 | 
			
		||||
        "torch/csrc/cudnn/Types.cpp",
 | 
			
		||||
        "torch/csrc/cudnn/Handles.cpp",
 | 
			
		||||
    ]
 | 
			
		||||
    extra_compile_args += ['-DWITH_CUDNN']
 | 
			
		||||
 | 
			
		||||
if DEBUG:
 | 
			
		||||
    extra_compile_args += ['-O0', '-g']
 | 
			
		||||
    extra_link_args += ['-O0', '-g']
 | 
			
		||||
 | 
			
		||||
if os.getenv('PYTORCH_BINARY_BUILD') and platform.system() == 'Linux':
 | 
			
		||||
    print('PYTORCH_BINARY_BUILD found. Static linking libstdc++ on Linux')
 | 
			
		||||
    # get path of libstdc++ and link manually.
 | 
			
		||||
    # for reasons unknown, -static-libstdc++ doesn't fully link some symbols
 | 
			
		||||
    CXXNAME = os.getenv('CXX', 'g++')
 | 
			
		||||
    STDCPP_LIB = subprocess.check_output([CXXNAME, '-print-file-name=libstdc++.a'])
 | 
			
		||||
    STDCPP_LIB = STDCPP_LIB[:-1]
 | 
			
		||||
    if type(STDCPP_LIB) != str:  # python 3
 | 
			
		||||
        STDCPP_LIB = STDCPP_LIB.decode(sys.stdout.encoding)
 | 
			
		||||
    main_link_args += [STDCPP_LIB]
 | 
			
		||||
    version_script = os.path.abspath("tools/pytorch.version")
 | 
			
		||||
    extra_link_args += ['-Wl,--version-script=' + version_script]
 | 
			
		||||
 | 
			
		||||
def make_relative_rpath(path):
 | 
			
		||||
    if platform.system() == 'Darwin':
 | 
			
		||||
        return '-Wl,-rpath,@loader_path/' + path
 | 
			
		||||
    else:
 | 
			
		||||
        return '-Wl,-rpath,$ORIGIN/' + path
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Declare extensions and package
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
extensions = []
 | 
			
		||||
packages = find_packages(exclude=('tools', 'tools.*',))
 | 
			
		||||
 | 
			
		||||
C = Extension("torch._C",
 | 
			
		||||
              libraries=main_libraries,
 | 
			
		||||
              sources=main_sources,
 | 
			
		||||
              language='c++',
 | 
			
		||||
              extra_compile_args=main_compile_args + extra_compile_args,
 | 
			
		||||
              include_dirs=include_dirs,
 | 
			
		||||
              library_dirs=library_dirs,
 | 
			
		||||
              extra_link_args=extra_link_args + main_link_args + [make_relative_rpath('lib')],
 | 
			
		||||
              )
 | 
			
		||||
extensions.append(C)
 | 
			
		||||
 | 
			
		||||
DL = Extension("torch._dl",
 | 
			
		||||
               sources=["torch/csrc/dl.c"],
 | 
			
		||||
               language='c',
 | 
			
		||||
               )
 | 
			
		||||
extensions.append(DL)
 | 
			
		||||
 | 
			
		||||
THNN = Extension("torch._thnn._THNN",
 | 
			
		||||
                 sources=['torch/csrc/nn/THNN.cpp'],
 | 
			
		||||
                 language='c++',
 | 
			
		||||
                 extra_compile_args=extra_compile_args,
 | 
			
		||||
                 include_dirs=include_dirs,
 | 
			
		||||
                 extra_link_args=extra_link_args + [
 | 
			
		||||
                     TH_LIB,
 | 
			
		||||
                     THNN_LIB,
 | 
			
		||||
                     make_relative_rpath('../lib'),
 | 
			
		||||
                 ]
 | 
			
		||||
                 )
 | 
			
		||||
extensions.append(THNN)
 | 
			
		||||
 | 
			
		||||
if WITH_CUDA:
 | 
			
		||||
    THCUNN = Extension("torch._thnn._THCUNN",
 | 
			
		||||
                       sources=['torch/csrc/nn/THCUNN.cpp'],
 | 
			
		||||
                       language='c++',
 | 
			
		||||
                       extra_compile_args=extra_compile_args,
 | 
			
		||||
                       include_dirs=include_dirs,
 | 
			
		||||
                       extra_link_args=extra_link_args + [
 | 
			
		||||
                           TH_LIB,
 | 
			
		||||
                           THC_LIB,
 | 
			
		||||
                           THCUNN_LIB,
 | 
			
		||||
                           make_relative_rpath('../lib'),
 | 
			
		||||
                       ]
 | 
			
		||||
                       )
 | 
			
		||||
    extensions.append(THCUNN)
 | 
			
		||||
 | 
			
		||||
version = '0.2.0'
 | 
			
		||||
if os.getenv('PYTORCH_BUILD_VERSION'):
 | 
			
		||||
    assert os.getenv('PYTORCH_BUILD_NUMBER') is not None
 | 
			
		||||
    version = os.getenv('PYTORCH_BUILD_VERSION') \
 | 
			
		||||
        + '_' + os.getenv('PYTORCH_BUILD_NUMBER')
 | 
			
		||||
else:
 | 
			
		||||
    try:
 | 
			
		||||
        sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=cwd).decode('ascii').strip()
 | 
			
		||||
        version += '+' + sha[:7]
 | 
			
		||||
    except subprocess.CalledProcessError:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
setup(name="torch", version=version,
 | 
			
		||||
      description="Tensors and Dynamic neural networks in Python with strong GPU acceleration",
 | 
			
		||||
      ext_modules=extensions,
 | 
			
		||||
      cmdclass={
 | 
			
		||||
          'build': build,
 | 
			
		||||
          'build_py': build_py,
 | 
			
		||||
          'build_ext': build_ext,
 | 
			
		||||
          'build_deps': build_deps,
 | 
			
		||||
          'build_module': build_module,
 | 
			
		||||
          'develop': develop,
 | 
			
		||||
          'install': install,
 | 
			
		||||
          'clean': clean,
 | 
			
		||||
      },
 | 
			
		||||
      packages=packages,
 | 
			
		||||
      package_data={'torch': [
 | 
			
		||||
          'lib/*.so*', 'lib/*.dylib*',
 | 
			
		||||
          'lib/torch_shm_manager',
 | 
			
		||||
          'lib/*.h',
 | 
			
		||||
          'lib/include/TH/*.h', 'lib/include/TH/generic/*.h',
 | 
			
		||||
          'lib/include/THC/*.h', 'lib/include/THC/generic/*.h']},
 | 
			
		||||
      install_requires=['pyyaml', 'numpy'],
 | 
			
		||||
      )
 | 
			
		||||
							
								
								
									
										291
									
								
								test/common.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								test/common.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,291 @@
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import argparse
 | 
			
		||||
import unittest
 | 
			
		||||
import warnings
 | 
			
		||||
import contextlib
 | 
			
		||||
from functools import wraps
 | 
			
		||||
from itertools import product
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
 | 
			
		||||
import torch
 | 
			
		||||
import torch.cuda
 | 
			
		||||
from torch.autograd import Variable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
torch.set_default_tensor_type('torch.DoubleTensor')
 | 
			
		||||
 | 
			
		||||
SEED = 0
 | 
			
		||||
SEED_SET = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_set_seed_once():
 | 
			
		||||
    global SEED
 | 
			
		||||
    global SEED_SET
 | 
			
		||||
    parser = argparse.ArgumentParser(add_help=False)
 | 
			
		||||
    parser.add_argument('--seed', type=int, default=123)
 | 
			
		||||
    args, remaining = parser.parse_known_args()
 | 
			
		||||
    if SEED_SET == 0:
 | 
			
		||||
        torch.manual_seed(args.seed)
 | 
			
		||||
        if torch.cuda.is_available():
 | 
			
		||||
            torch.cuda.manual_seed_all(args.seed)
 | 
			
		||||
        SEED = args.seed
 | 
			
		||||
        SEED_SET = 1
 | 
			
		||||
    remaining = [sys.argv[0]] + remaining
 | 
			
		||||
    return remaining
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_tests():
 | 
			
		||||
    remaining = parse_set_seed_once()
 | 
			
		||||
    unittest.main(argv=remaining)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_NUMPY = True
 | 
			
		||||
try:
 | 
			
		||||
    import numpy
 | 
			
		||||
except ImportError:
 | 
			
		||||
    TEST_NUMPY = False
 | 
			
		||||
 | 
			
		||||
TEST_SCIPY = True
 | 
			
		||||
try:
 | 
			
		||||
    import scipy
 | 
			
		||||
except ImportError:
 | 
			
		||||
    TEST_SCIPY = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def skipIfNoLapack(fn):
 | 
			
		||||
    @wraps(fn)
 | 
			
		||||
    def wrapper(*args, **kwargs):
 | 
			
		||||
        try:
 | 
			
		||||
            fn(*args, **kwargs)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            if 'Lapack library not found' in e.args[0]:
 | 
			
		||||
                raise unittest.SkipTest('Compiled without Lapack')
 | 
			
		||||
            raise
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def suppress_warnings(fn):
 | 
			
		||||
    def wrapper(*args, **kwargs):
 | 
			
		||||
        with warnings.catch_warnings():
 | 
			
		||||
            warnings.simplefilter("ignore")
 | 
			
		||||
            fn(*args, **kwargs)
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_cpu_type(t):
 | 
			
		||||
    assert t.__module__ == 'torch.cuda'
 | 
			
		||||
    return getattr(torch, t.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_gpu_type(t):
 | 
			
		||||
    assert t.__module__ == 'torch'
 | 
			
		||||
    return getattr(torch.cuda, t.__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_gpu(obj, type_map={}):
 | 
			
		||||
    if torch.is_tensor(obj):
 | 
			
		||||
        t = type_map.get(type(obj), get_gpu_type(type(obj)))
 | 
			
		||||
        return obj.clone().type(t)
 | 
			
		||||
    elif torch.is_storage(obj):
 | 
			
		||||
        return obj.new().resize_(obj.size()).copy_(obj)
 | 
			
		||||
    elif isinstance(obj, Variable):
 | 
			
		||||
        assert obj.is_leaf
 | 
			
		||||
        t = type_map.get(type(obj.data), get_gpu_type(type(obj.data)))
 | 
			
		||||
        return Variable(obj.data.clone().type(t), requires_grad=obj.requires_grad)
 | 
			
		||||
    elif isinstance(obj, list):
 | 
			
		||||
        return [to_gpu(o, type_map) for o in obj]
 | 
			
		||||
    elif isinstance(obj, tuple):
 | 
			
		||||
        return tuple(to_gpu(o, type_map) for o in obj)
 | 
			
		||||
    else:
 | 
			
		||||
        return deepcopy(obj)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextlib.contextmanager
 | 
			
		||||
def freeze_rng_state():
 | 
			
		||||
    rng_state = torch.get_rng_state()
 | 
			
		||||
    if torch.cuda.is_available():
 | 
			
		||||
        cuda_rng_state = torch.cuda.get_rng_state()
 | 
			
		||||
    yield
 | 
			
		||||
    if torch.cuda.is_available():
 | 
			
		||||
        torch.cuda.set_rng_state(cuda_rng_state)
 | 
			
		||||
    torch.set_rng_state(rng_state)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def iter_indices(tensor):
 | 
			
		||||
    if tensor.dim() == 0:
 | 
			
		||||
        return range(0)
 | 
			
		||||
    if tensor.dim() == 1:
 | 
			
		||||
        return range(tensor.size(0))
 | 
			
		||||
    return product(*(range(s) for s in tensor.size()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_iterable(obj):
 | 
			
		||||
    try:
 | 
			
		||||
        iter(obj)
 | 
			
		||||
        return True
 | 
			
		||||
    except:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCase(unittest.TestCase):
 | 
			
		||||
    precision = 1e-5
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        torch.manual_seed(SEED)
 | 
			
		||||
        if torch.cuda.is_available():
 | 
			
		||||
            torch.cuda.manual_seed_all(SEED)
 | 
			
		||||
 | 
			
		||||
    def assertTensorsSlowEqual(self, x, y, prec=None, message=''):
 | 
			
		||||
        max_err = 0
 | 
			
		||||
        self.assertEqual(x.size(), y.size())
 | 
			
		||||
        for index in iter_indices(x):
 | 
			
		||||
            max_err = max(max_err, abs(x[index] - y[index]))
 | 
			
		||||
        self.assertLessEqual(max_err, prec, message)
 | 
			
		||||
 | 
			
		||||
    def safeCoalesce(self, t):
 | 
			
		||||
        tc = t.coalesce()
 | 
			
		||||
 | 
			
		||||
        value_map = {}
 | 
			
		||||
        for idx, val in zip(t._indices().t(), t._values()):
 | 
			
		||||
            idx_tup = tuple(idx)
 | 
			
		||||
            if idx_tup in value_map:
 | 
			
		||||
                value_map[idx_tup] += val
 | 
			
		||||
            else:
 | 
			
		||||
                value_map[idx_tup] = val.clone() if torch.is_tensor(val) else val
 | 
			
		||||
 | 
			
		||||
        new_indices = sorted(list(value_map.keys()))
 | 
			
		||||
        new_values = [value_map[idx] for idx in new_indices]
 | 
			
		||||
        if t._values().ndimension() < 2:
 | 
			
		||||
            new_values = t._values().new(new_values)
 | 
			
		||||
        else:
 | 
			
		||||
            new_values = torch.stack(new_values)
 | 
			
		||||
 | 
			
		||||
        new_indices = t._indices().new(new_indices).t()
 | 
			
		||||
        tg = t.new(new_indices, new_values, t.size())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(tc._indices(), tg._indices())
 | 
			
		||||
        self.assertEqual(tc._values(), tg._values())
 | 
			
		||||
 | 
			
		||||
        return tg
 | 
			
		||||
 | 
			
		||||
    def unwrapVariables(self, x, y):
 | 
			
		||||
        if isinstance(x, Variable) and isinstance(y, Variable):
 | 
			
		||||
            return x.data, y.data
 | 
			
		||||
        elif isinstance(x, Variable) or isinstance(y, Variable):
 | 
			
		||||
            raise AssertionError("cannot compare {} and {}".format(type(x), type(y)))
 | 
			
		||||
        return x, y
 | 
			
		||||
 | 
			
		||||
    def assertEqual(self, x, y, prec=None, message=''):
 | 
			
		||||
        if prec is None:
 | 
			
		||||
            prec = self.precision
 | 
			
		||||
 | 
			
		||||
        x, y = self.unwrapVariables(x, y)
 | 
			
		||||
 | 
			
		||||
        if torch.is_tensor(x) and torch.is_tensor(y):
 | 
			
		||||
            def assertTensorsEqual(a, b):
 | 
			
		||||
                super(TestCase, self).assertEqual(a.size(), b.size())
 | 
			
		||||
                if a.numel() > 0:
 | 
			
		||||
                    b = b.type_as(a)
 | 
			
		||||
                    b = b.cuda(device=a.get_device()) if a.is_cuda else b.cpu()
 | 
			
		||||
                    # check that NaNs are in the same locations
 | 
			
		||||
                    nan_mask = a != a
 | 
			
		||||
                    self.assertTrue(torch.equal(nan_mask, b != b))
 | 
			
		||||
                    diff = a - b
 | 
			
		||||
                    diff[nan_mask] = 0
 | 
			
		||||
                    if diff.is_signed():
 | 
			
		||||
                        diff = diff.abs()
 | 
			
		||||
                    max_err = diff.max()
 | 
			
		||||
                    self.assertLessEqual(max_err, prec, message)
 | 
			
		||||
            self.assertEqual(x.is_sparse, y.is_sparse, message)
 | 
			
		||||
            if x.is_sparse:
 | 
			
		||||
                x = self.safeCoalesce(x)
 | 
			
		||||
                y = self.safeCoalesce(y)
 | 
			
		||||
                assertTensorsEqual(x._indices(), y._indices())
 | 
			
		||||
                assertTensorsEqual(x._values(), y._values())
 | 
			
		||||
            else:
 | 
			
		||||
                assertTensorsEqual(x, y)
 | 
			
		||||
        elif type(x) == str and type(y) == str:
 | 
			
		||||
            super(TestCase, self).assertEqual(x, y)
 | 
			
		||||
        elif type(x) == set and type(y) == set:
 | 
			
		||||
            super(TestCase, self).assertEqual(x, y)
 | 
			
		||||
        elif is_iterable(x) and is_iterable(y):
 | 
			
		||||
            super(TestCase, self).assertEqual(len(x), len(y))
 | 
			
		||||
            for x_, y_ in zip(x, y):
 | 
			
		||||
                self.assertEqual(x_, y_, prec, message)
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                self.assertLessEqual(abs(x - y), prec, message)
 | 
			
		||||
                return
 | 
			
		||||
            except:
 | 
			
		||||
                pass
 | 
			
		||||
            super(TestCase, self).assertEqual(x, y, message)
 | 
			
		||||
 | 
			
		||||
    def assertNotEqual(self, x, y, prec=None, message=''):
 | 
			
		||||
        if prec is None:
 | 
			
		||||
            prec = self.precision
 | 
			
		||||
 | 
			
		||||
        x, y = self.unwrapVariables(x, y)
 | 
			
		||||
 | 
			
		||||
        if torch.is_tensor(x) and torch.is_tensor(y):
 | 
			
		||||
            if x.size() != y.size():
 | 
			
		||||
                super(TestCase, self).assertNotEqual(x.size(), y.size())
 | 
			
		||||
            self.assertGreater(x.numel(), 0)
 | 
			
		||||
            y = y.type_as(x)
 | 
			
		||||
            y = y.cuda(device=x.get_device()) if x.is_cuda else y.cpu()
 | 
			
		||||
            nan_mask = x != x
 | 
			
		||||
            if torch.equal(nan_mask, y != y):
 | 
			
		||||
                diff = x - y
 | 
			
		||||
                if diff.is_signed():
 | 
			
		||||
                    diff = diff.abs()
 | 
			
		||||
                diff[nan_mask] = 0
 | 
			
		||||
                max_err = diff.max()
 | 
			
		||||
                self.assertGreaterEqual(max_err, prec, message)
 | 
			
		||||
        elif type(x) == str and type(y) == str:
 | 
			
		||||
            super(TestCase, self).assertNotEqual(x, y)
 | 
			
		||||
        elif is_iterable(x) and is_iterable(y):
 | 
			
		||||
            super(TestCase, self).assertNotEqual(x, y)
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                self.assertGreaterEqual(abs(x - y), prec, message)
 | 
			
		||||
                return
 | 
			
		||||
            except:
 | 
			
		||||
                pass
 | 
			
		||||
            super(TestCase, self).assertNotEqual(x, y, message)
 | 
			
		||||
 | 
			
		||||
    def assertObjectIn(self, obj, iterable):
 | 
			
		||||
        for elem in iterable:
 | 
			
		||||
            if id(obj) == id(elem):
 | 
			
		||||
                return
 | 
			
		||||
        raise AssertionError("object not found in iterable")
 | 
			
		||||
 | 
			
		||||
    if sys.version_info < (3, 2):
 | 
			
		||||
        # assertRaisesRegexp renamed assertRaisesRegex in 3.2
 | 
			
		||||
        assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def download_file(url, binary=True):
 | 
			
		||||
    if sys.version_info < (3,):
 | 
			
		||||
        from urlparse import urlsplit
 | 
			
		||||
        import urllib2
 | 
			
		||||
        request = urllib2
 | 
			
		||||
        error = urllib2
 | 
			
		||||
    else:
 | 
			
		||||
        from urllib.parse import urlsplit
 | 
			
		||||
        from urllib import request, error
 | 
			
		||||
 | 
			
		||||
    filename = os.path.basename(urlsplit(url)[2])
 | 
			
		||||
    data_dir = os.path.join(os.path.dirname(__file__), 'data')
 | 
			
		||||
    path = os.path.join(data_dir, filename)
 | 
			
		||||
 | 
			
		||||
    if os.path.exists(path):
 | 
			
		||||
        return path
 | 
			
		||||
    try:
 | 
			
		||||
        data = request.urlopen(url, timeout=15).read()
 | 
			
		||||
        with open(path, 'wb' if binary else 'w') as f:
 | 
			
		||||
            f.write(data)
 | 
			
		||||
        return path
 | 
			
		||||
    except error.URLError:
 | 
			
		||||
        msg = "could not download test file '{}'".format(url)
 | 
			
		||||
        warnings.warn(msg, RuntimeWarning)
 | 
			
		||||
        raise unittest.SkipTest(msg)
 | 
			
		||||
							
								
								
									
										784
									
								
								test/common_nn.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										784
									
								
								test/common_nn.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,784 @@
 | 
			
		||||
import sys
 | 
			
		||||
import tempfile
 | 
			
		||||
import unittest
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from itertools import product
 | 
			
		||||
 | 
			
		||||
import torch
 | 
			
		||||
import torch.cuda
 | 
			
		||||
from torch.autograd import Variable
 | 
			
		||||
from common import TestCase, to_gpu, freeze_rng_state
 | 
			
		||||
from torch.autograd.gradcheck import get_numerical_jacobian, iter_tensors, contiguous
 | 
			
		||||
import torch.backends.cudnn
 | 
			
		||||
 | 
			
		||||
# tarfile module tries to obtain a file object name in python 3.3
 | 
			
		||||
if sys.version_info[:2] == (3, 3):
 | 
			
		||||
    TemporaryFile = tempfile.NamedTemporaryFile
 | 
			
		||||
else:
 | 
			
		||||
    TemporaryFile = tempfile.TemporaryFile
 | 
			
		||||
 | 
			
		||||
TEST_CUDA = torch.cuda.is_available()
 | 
			
		||||
TEST_MULTIGPU = TEST_CUDA and torch.cuda.device_count() >= 2
 | 
			
		||||
TEST_CUDNN = TEST_CUDA and torch.backends.cudnn.is_acceptable(torch.cuda.FloatTensor(1))
 | 
			
		||||
TEST_CUDNN_VERSION = TEST_CUDNN and torch.backends.cudnn.version()
 | 
			
		||||
PRECISION = 1e-5
 | 
			
		||||
 | 
			
		||||
module_tests = [
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Linear',
 | 
			
		||||
        constructor_args=(10, 8),
 | 
			
		||||
        input_size=(4, 10),
 | 
			
		||||
        reference_fn=lambda i, p: torch.mm(i, p[0].t()) + p[1].view(1, -1).expand(4, 8)
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Linear',
 | 
			
		||||
        constructor_args=(10, 8, False),
 | 
			
		||||
        input_size=(4, 10),
 | 
			
		||||
        desc='no_bias',
 | 
			
		||||
        reference_fn=lambda i, p: torch.mm(i, p[0].t())
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Threshold',
 | 
			
		||||
        constructor_args=(2, 1),
 | 
			
		||||
        input_size=(2, 3, 4, 5),
 | 
			
		||||
        check_inplace=True,
 | 
			
		||||
        desc='threshold_value'
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Threshold',
 | 
			
		||||
        constructor_args=(2, 10),
 | 
			
		||||
        input_size=(2, 3, 4, 5),
 | 
			
		||||
        desc='large_value'
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='ReLU',
 | 
			
		||||
        input_size=(2, 3, 4, 5),
 | 
			
		||||
        check_inplace=True,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='ReLU6',
 | 
			
		||||
        input_size=(2, 3, 4, 5),
 | 
			
		||||
        check_inplace=True,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='RReLU',
 | 
			
		||||
        input_size=(1, 2, 2),
 | 
			
		||||
        test_cuda=False,
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='RReLU',
 | 
			
		||||
        constructor_args=(0.1, 0.9),
 | 
			
		||||
        input_size=(4, 4, 5),
 | 
			
		||||
        desc='with_up_down',
 | 
			
		||||
        test_cuda=False,
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Hardtanh',
 | 
			
		||||
        input_size=(3, 2, 5),
 | 
			
		||||
        reference_fn=lambda i, _: i.clamp(-1, 1),
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Sigmoid',
 | 
			
		||||
        input_size=(2, 3, 4, 5)
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Tanh',
 | 
			
		||||
        input_size=(2, 3, 4, 5)
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softmax',
 | 
			
		||||
        input_size=(10, 20),
 | 
			
		||||
        reference_fn=lambda i, _: torch.exp(i).div(torch.exp(i).sum(1, True).expand(10, 20)),
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softmax2d',
 | 
			
		||||
        input_size=(1, 3, 10, 20),
 | 
			
		||||
        reference_fn=lambda i, _: torch.exp(i).div(torch.exp(i).sum(1, False)),
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='LogSoftmax',
 | 
			
		||||
        input_size=(10, 20),
 | 
			
		||||
        reference_fn=lambda i, _: torch.exp(i).div_(torch.exp(i).sum(1, True).expand(10, 20)).log_(),
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='LogSoftmax',
 | 
			
		||||
        input_size=(1, 3, 10, 20),
 | 
			
		||||
        reference_fn=lambda i, _: torch.exp(i).div_(torch.exp(i).sum(1, False)).log_(),
 | 
			
		||||
        desc='multiparam',
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='ELU',
 | 
			
		||||
        constructor_args=(2.,),
 | 
			
		||||
        input_size=(3, 2, 5),
 | 
			
		||||
    ),
 | 
			
		||||
    # TODO: reference function
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Hardshrink',
 | 
			
		||||
        constructor_args=(2.,),
 | 
			
		||||
        input_size=(4, 3, 2, 4),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='LeakyReLU',
 | 
			
		||||
        input_size=(3, 2, 5),
 | 
			
		||||
        check_inplace=True
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='LeakyReLU',
 | 
			
		||||
        constructor_args=(0.5,),
 | 
			
		||||
        input_size=(3, 2, 5),
 | 
			
		||||
        check_inplace=True,
 | 
			
		||||
        desc='with_negval'
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='LogSigmoid',
 | 
			
		||||
        input_size=(2, 3, 4),
 | 
			
		||||
        reference_fn=lambda i, _: i.sigmoid().log(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softplus',
 | 
			
		||||
        input_size=(10, 20),
 | 
			
		||||
        reference_fn=lambda i, _: torch.log(1 + torch.exp(i)),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softplus',
 | 
			
		||||
        constructor_args=(2,),
 | 
			
		||||
        input_size=(10, 20),
 | 
			
		||||
        reference_fn=lambda i, _: 1. / 2. * torch.log(1 + torch.exp(2 * i)),
 | 
			
		||||
        desc='beta',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softshrink',
 | 
			
		||||
        input_size=(3, 2, 5),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softshrink',
 | 
			
		||||
        constructor_args=(1,),
 | 
			
		||||
        input_size=(3, 2, 5),
 | 
			
		||||
        desc='lambda',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='CrossMapLRN2d',
 | 
			
		||||
        constructor_args=(5, 5e-3, 1e-3, 2),
 | 
			
		||||
        input_size=(2, 3, 6, 6),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='PReLU',
 | 
			
		||||
        input_size=(2, 3, 4),
 | 
			
		||||
        reference_fn=lambda i, p: torch.clamp(i, min=0) + torch.clamp(i, max=0) * p[0][0],
 | 
			
		||||
        desc='1d',
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='PReLU',
 | 
			
		||||
        constructor_args=(3,),
 | 
			
		||||
        input_size=(2, 3, 4),
 | 
			
		||||
        desc='1d_multiparam',
 | 
			
		||||
        reference_fn=lambda i, p: torch.clamp(i, min=0) + torch.clamp(i, max=0) * p[0][0],
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='PReLU',
 | 
			
		||||
        input_size=(2, 3, 4, 5),
 | 
			
		||||
        desc='2d',
 | 
			
		||||
        reference_fn=lambda i, p: torch.clamp(i, min=0) + torch.clamp(i, max=0) * p[0][0],
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='PReLU',
 | 
			
		||||
        constructor_args=(3,),
 | 
			
		||||
        input_size=(2, 3, 4, 5),
 | 
			
		||||
        desc='2d_multiparam',
 | 
			
		||||
        reference_fn=lambda i, p: torch.clamp(i, min=0) + torch.clamp(i, max=0) * p[0][0],
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='PReLU',
 | 
			
		||||
        input_size=(2, 3, 4, 5, 6),
 | 
			
		||||
        reference_fn=lambda i, p: torch.clamp(i, min=0) + torch.clamp(i, max=0) * p[0][0],
 | 
			
		||||
        desc='3d',
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='PReLU',
 | 
			
		||||
        constructor_args=(3,),
 | 
			
		||||
        input_size=(2, 3, 4, 5, 6),
 | 
			
		||||
        desc='3d_multiparam',
 | 
			
		||||
        reference_fn=lambda i, p: torch.clamp(i, min=0) + torch.clamp(i, max=0) * p[0][0],
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softsign',
 | 
			
		||||
        input_size=(3, 2, 5),
 | 
			
		||||
        reference_fn=lambda i, _: i.div(1 + torch.abs(i)),
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Softmin',
 | 
			
		||||
        input_size=(10, 20),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='Tanhshrink',
 | 
			
		||||
        input_size=(2, 3, 4, 5)
 | 
			
		||||
    ),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
criterion_tests = [
 | 
			
		||||
    dict(module_name='L1Loss',
 | 
			
		||||
         input_size=(2, 3, 4),
 | 
			
		||||
         target=torch.randn(2, 3, 4),
 | 
			
		||||
         reference_fn=lambda i, t, _: 1. / i.numel() *
 | 
			
		||||
         sum((a - b).abs().sum() for a, b in zip(i, t)),
 | 
			
		||||
         ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss',
 | 
			
		||||
        input=torch.rand(15, 10).log(),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10).floor().long(),
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss',
 | 
			
		||||
        constructor_args=(None, False),
 | 
			
		||||
        input=torch.rand(15, 10).log(),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10).floor().long(),
 | 
			
		||||
        desc='no_size_average'
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss',
 | 
			
		||||
        constructor_args=(None, True, 2),
 | 
			
		||||
        input=torch.rand(15, 10).log(),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10).floor().long(),
 | 
			
		||||
        desc='ignore_index'
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss',
 | 
			
		||||
        constructor_args=(torch.rand(10),),
 | 
			
		||||
        input=torch.rand(15, 10).add(1e-2).log(),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10).floor().long(),
 | 
			
		||||
        desc='weights',
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss',
 | 
			
		||||
        constructor_args=(torch.rand(10), True, 2),
 | 
			
		||||
        input=torch.rand(15, 10).add(1e-2).log(),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10).floor().long(),
 | 
			
		||||
        desc='weights_ignore_index'
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss',
 | 
			
		||||
        constructor_args=(torch.rand(10), True, -1),
 | 
			
		||||
        input=torch.rand(15, 10).add(1e-2).log(),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10 + 1).floor().long() - 1,
 | 
			
		||||
        desc='weights_ignore_index_neg'
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='KLDivLoss',
 | 
			
		||||
        input=torch.rand(10, 10).log(),
 | 
			
		||||
        target=torch.rand(10, 10),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='MSELoss',
 | 
			
		||||
        input=torch.randn(2, 3, 4, 5),
 | 
			
		||||
        target=torch.randn(2, 3, 4, 5),
 | 
			
		||||
        reference_fn=lambda i, t, _: (i - t).abs().pow(2).sum() / i.numel(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='BCELoss',
 | 
			
		||||
        input=torch.rand(15, 10).clamp_(1e-2, 1 - 1e-2),
 | 
			
		||||
        target=torch.randn(15, 10).gt(0).double(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='BCELoss',
 | 
			
		||||
        constructor_args=(torch.rand(10),),
 | 
			
		||||
        input=torch.rand(15, 10).clamp_(1e-2, 1 - 1e-2),
 | 
			
		||||
        target=torch.randn(15, 10).gt(0).double(),
 | 
			
		||||
        desc='weights',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='CrossEntropyLoss',
 | 
			
		||||
        input=torch.randn(15, 10),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10).floor().long(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='CrossEntropyLoss',
 | 
			
		||||
        constructor_args=(torch.rand(10),),
 | 
			
		||||
        input=torch.randn(15, 10),
 | 
			
		||||
        target=torch.Tensor(15).uniform_().mul(10).floor().long(),
 | 
			
		||||
        desc='weights',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss2d',
 | 
			
		||||
        input_size=(2, 3, 5, 5),
 | 
			
		||||
        target=torch.rand(2, 5, 5).mul(3).floor().long(),
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss2d',
 | 
			
		||||
        constructor_args=(torch.rand(3),),
 | 
			
		||||
        input_size=(2, 3, 5, 5),
 | 
			
		||||
        target=torch.rand(2, 5, 5).mul(3).floor().long(),
 | 
			
		||||
        desc='weights',
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='NLLLoss2d',
 | 
			
		||||
        constructor_args=(None, True, 3),
 | 
			
		||||
        input_size=(2, 3, 5, 5),
 | 
			
		||||
        target=torch.rand(2, 5, 5).mul(4).floor().long(),
 | 
			
		||||
        desc='ignore_index',
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='HingeEmbeddingLoss',
 | 
			
		||||
        input=torch.rand(10),
 | 
			
		||||
        target=torch.randn(10).gt(0).double().mul_(2).sub(1),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='HingeEmbeddingLoss',
 | 
			
		||||
        constructor_args=(0.5,),
 | 
			
		||||
        input=torch.rand(10),
 | 
			
		||||
        target=torch.randn(10).gt(0).double().mul_(2).sub(1),
 | 
			
		||||
        desc='margin',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='MultiLabelMarginLoss',
 | 
			
		||||
        input_size=(5, 10),
 | 
			
		||||
        target=torch.rand(5, 10).mul(10).floor().long(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='MultiLabelSoftMarginLoss',
 | 
			
		||||
        input_size=(5, 10),
 | 
			
		||||
        target=torch.rand(5, 10).mul(2).floor(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='MultiLabelSoftMarginLoss',
 | 
			
		||||
        constructor_args=(torch.rand(10),),
 | 
			
		||||
        input_size=(5, 10),
 | 
			
		||||
        target=torch.rand(5, 10).mul(2).floor(),
 | 
			
		||||
        desc='weights',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='MultiMarginLoss',
 | 
			
		||||
        input_size=(5, 10),
 | 
			
		||||
        target=torch.rand(5).mul(8).floor().long(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='SmoothL1Loss',
 | 
			
		||||
        input_size=(5, 10),
 | 
			
		||||
        target=torch.randn(5, 10),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='SoftMarginLoss',
 | 
			
		||||
        input_size=(5, 5),
 | 
			
		||||
        target=torch.randn(5, 5).sign(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='CosineEmbeddingLoss',
 | 
			
		||||
        input=(torch.rand(15, 10), torch.rand(15, 10)),
 | 
			
		||||
        target=torch.randn(15).sign(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='CosineEmbeddingLoss',
 | 
			
		||||
        constructor_args=(0.7,),
 | 
			
		||||
        input=(torch.rand(15, 10), torch.rand(15, 10)),
 | 
			
		||||
        target=torch.randn(15).sign(),
 | 
			
		||||
        desc='margin',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='MarginRankingLoss',
 | 
			
		||||
        input=(torch.randn(50).mul(10), torch.randn(50).mul(10)),
 | 
			
		||||
        target=torch.randn(50).sign(),
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
    dict(
 | 
			
		||||
        module_name='MarginRankingLoss',
 | 
			
		||||
        constructor_args=(2,),
 | 
			
		||||
        input=(torch.randn(50).mul(10), torch.randn(50).mul(10)),
 | 
			
		||||
        target=torch.randn(50).sign(),
 | 
			
		||||
        desc='margin',
 | 
			
		||||
        check_gradgrad=False,
 | 
			
		||||
    ),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NNTestCase(TestCase):
 | 
			
		||||
 | 
			
		||||
    def _jacobian(self, input, num_out):
 | 
			
		||||
        if isinstance(input, tuple):
 | 
			
		||||
            return tuple(self._jacobian(elem, num_out) for elem in input)
 | 
			
		||||
        elif isinstance(input, list):
 | 
			
		||||
            return [self._jacobian(elem, num_out) for elem in input]
 | 
			
		||||
        else:
 | 
			
		||||
            return torch.zeros(input.nelement(), num_out)
 | 
			
		||||
 | 
			
		||||
    def _flatten_tensors(self, x):
 | 
			
		||||
        if torch.is_tensor(x):
 | 
			
		||||
            if x.is_sparse:
 | 
			
		||||
                return x.to_dense().view(-1)
 | 
			
		||||
            else:
 | 
			
		||||
                return x.view(-1)
 | 
			
		||||
        elif isinstance(x, Variable):
 | 
			
		||||
            return self._flatten_tensors(x.data)
 | 
			
		||||
        else:
 | 
			
		||||
            return tuple(self._flatten_tensors(a) for a in x)
 | 
			
		||||
 | 
			
		||||
    def _zero_grad_input(self, input):
 | 
			
		||||
        if isinstance(input, Variable):
 | 
			
		||||
            if input.requires_grad and input.grad is not None:
 | 
			
		||||
                input.grad.data.zero_()
 | 
			
		||||
                input.grad.detach_()
 | 
			
		||||
        elif torch.is_tensor(input):
 | 
			
		||||
            return
 | 
			
		||||
        else:
 | 
			
		||||
            for i in input:
 | 
			
		||||
                self._zero_grad_input(i)
 | 
			
		||||
 | 
			
		||||
    def _analytical_jacobian(self, module, input, jacobian_input=True, jacobian_parameters=True):
 | 
			
		||||
        output = self._forward(module, input)
 | 
			
		||||
        output_t = output.data if isinstance(output, Variable) else output
 | 
			
		||||
        d_out = output_t.new().resize_(output_t.size())
 | 
			
		||||
        flat_d_out = d_out.view(-1)
 | 
			
		||||
 | 
			
		||||
        if jacobian_input:
 | 
			
		||||
            jacobian_inp = self._jacobian(input, d_out.nelement())
 | 
			
		||||
            flat_jacobian_input = list(iter_tensors(jacobian_inp))
 | 
			
		||||
 | 
			
		||||
        if jacobian_parameters:
 | 
			
		||||
            param, d_param = self._get_parameters(module)
 | 
			
		||||
            num_param = sum(p.numel() for p in param)
 | 
			
		||||
            jacobian_param = torch.zeros(num_param, d_out.nelement())
 | 
			
		||||
 | 
			
		||||
        for i in range(flat_d_out.nelement()):
 | 
			
		||||
            d_out.zero_()
 | 
			
		||||
            flat_d_out[i] = 1
 | 
			
		||||
 | 
			
		||||
            if jacobian_parameters:
 | 
			
		||||
                self._zero_grad_parameters(module)
 | 
			
		||||
            # Variables will accumulate gradient from multiple steps
 | 
			
		||||
            if jacobian_input:
 | 
			
		||||
                self._zero_grad_input(input)
 | 
			
		||||
            d_input = self._backward(module, input, output, d_out)
 | 
			
		||||
 | 
			
		||||
            if jacobian_input:
 | 
			
		||||
                for jacobian_x, d_x in zip(flat_jacobian_input, iter_tensors(d_input)):
 | 
			
		||||
                    jacobian_x[:, i] = d_x
 | 
			
		||||
            if jacobian_parameters:
 | 
			
		||||
                jacobian_param[:, i] = torch.cat(self._flatten_tensors(d_param), 0)
 | 
			
		||||
 | 
			
		||||
        res = tuple()
 | 
			
		||||
        if jacobian_input:
 | 
			
		||||
            res += jacobian_inp,
 | 
			
		||||
        if jacobian_parameters:
 | 
			
		||||
            res += jacobian_param,
 | 
			
		||||
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def _numerical_jacobian(self, module, input, jacobian_input=True, jacobian_parameters=True):
 | 
			
		||||
        output = self._forward(module, input)
 | 
			
		||||
        output_size = output.nelement()
 | 
			
		||||
 | 
			
		||||
        if jacobian_parameters:
 | 
			
		||||
            param, d_param = self._get_parameters(module)
 | 
			
		||||
 | 
			
		||||
        def fw(input):
 | 
			
		||||
            out = self._forward(module, input)
 | 
			
		||||
            if isinstance(out, Variable):
 | 
			
		||||
                return out.data
 | 
			
		||||
            return out
 | 
			
		||||
 | 
			
		||||
        res = tuple()
 | 
			
		||||
        input = contiguous(input)
 | 
			
		||||
        if jacobian_input:
 | 
			
		||||
            res += get_numerical_jacobian(fw, input, input, eps=1e-6),
 | 
			
		||||
        if jacobian_parameters:
 | 
			
		||||
            res += torch.cat(list(get_numerical_jacobian(fw, input, p, eps=1e-6) for p in param), 0),
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def check_jacobian(self, module, input, jacobian_input=True):
 | 
			
		||||
        jacobian_parameters = bool(self._get_parameters(module)[0])
 | 
			
		||||
        analytical = self._analytical_jacobian(module, input, jacobian_input, jacobian_parameters)
 | 
			
		||||
        numerical = self._numerical_jacobian(module, input, jacobian_input, jacobian_parameters)
 | 
			
		||||
        analytical_t = iter_tensors(analytical)
 | 
			
		||||
        numerical_t = iter_tensors(numerical)
 | 
			
		||||
        # TODO: compare structure
 | 
			
		||||
        self.assertLessEqual(
 | 
			
		||||
            max(a.add(-1, n).abs().max() for a, n in zip(analytical_t, numerical_t)),
 | 
			
		||||
            PRECISION
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def check_criterion_jacobian(self, criterion, input, target):
 | 
			
		||||
        eps = 1e-6
 | 
			
		||||
        self._forward_criterion(criterion, input, target)
 | 
			
		||||
        analytical_d_x = self._backward_criterion(criterion, input, target)
 | 
			
		||||
        numerical_d_x = deepcopy(analytical_d_x)
 | 
			
		||||
 | 
			
		||||
        input_t = iter_tensors(input)
 | 
			
		||||
        numerical_t = iter_tensors(numerical_d_x)
 | 
			
		||||
        for x, d_x in zip(input_t, numerical_t):
 | 
			
		||||
            x = x.view(-1)
 | 
			
		||||
            d_x = d_x.view(-1)
 | 
			
		||||
            for i in range(x.nelement()):
 | 
			
		||||
                original = x[i]
 | 
			
		||||
                x[i] = original + eps
 | 
			
		||||
                fx1 = self._forward_criterion(criterion, input, target)
 | 
			
		||||
                x[i] = original - eps
 | 
			
		||||
                fx2 = self._forward_criterion(criterion, input, target)
 | 
			
		||||
                deriv = (fx1 - fx2) / (2. * eps)
 | 
			
		||||
                d_x[i] = deriv
 | 
			
		||||
                x[i] = original
 | 
			
		||||
 | 
			
		||||
        # TODO: check structure
 | 
			
		||||
        analytical_t = iter_tensors(analytical_d_x)
 | 
			
		||||
        numerical_t = iter_tensors(numerical_d_x)
 | 
			
		||||
        self.assertLessEqual(
 | 
			
		||||
            max(a.add(-1, n).abs().max() for a, n in zip(analytical_t, numerical_t)),
 | 
			
		||||
            PRECISION
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestBase(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, constructor, constructor_args=tuple(), input_size=None,
 | 
			
		||||
                 input=None, desc='', reference_fn=None, fullname=None, **kwargs):
 | 
			
		||||
        if input_size is None and input is None:
 | 
			
		||||
            raise RuntimeError("Specify either an input tensor, or it's size!")
 | 
			
		||||
        self.constructor = constructor
 | 
			
		||||
        self.constructor_args = constructor_args
 | 
			
		||||
        self.input = input
 | 
			
		||||
        self.input_size = input_size
 | 
			
		||||
        self.desc = desc
 | 
			
		||||
        self.fullname = fullname
 | 
			
		||||
        self.reference_fn = reference_fn
 | 
			
		||||
 | 
			
		||||
    def get_name(self):
 | 
			
		||||
        if self.fullname is not None:
 | 
			
		||||
            return 'test_' + self.fullname
 | 
			
		||||
 | 
			
		||||
        test_name = 'test_' + self.constructor.__name__
 | 
			
		||||
        if self.desc:
 | 
			
		||||
            test_name += '_' + self.desc
 | 
			
		||||
        return test_name
 | 
			
		||||
 | 
			
		||||
    def _unpack_input(self, input):
 | 
			
		||||
        if isinstance(input, Variable):
 | 
			
		||||
            return input.data
 | 
			
		||||
        elif torch.is_tensor(input):
 | 
			
		||||
            return input
 | 
			
		||||
        else:
 | 
			
		||||
            return type(input)(self._unpack_input(i) for i in input)
 | 
			
		||||
 | 
			
		||||
    def _get_input(self):
 | 
			
		||||
        if self.input is not None:
 | 
			
		||||
            return self.input
 | 
			
		||||
 | 
			
		||||
        def map_input_sizes(sizes):
 | 
			
		||||
            if isinstance(sizes, list):
 | 
			
		||||
                return [map_input_sizes(s) for s in sizes]
 | 
			
		||||
            elif torch.is_tensor(sizes):
 | 
			
		||||
                return sizes.double()
 | 
			
		||||
            else:
 | 
			
		||||
                return torch.randn(*sizes)
 | 
			
		||||
 | 
			
		||||
        assert self.input_size is not None
 | 
			
		||||
        return map_input_sizes(self.input_size)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, test_case):
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ModuleTest(TestBase):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(ModuleTest, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.jacobian_input = kwargs.get('jacobian_input', True)
 | 
			
		||||
        self.should_test_cuda = kwargs.get('test_cuda', True)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, test_case):
 | 
			
		||||
        module = self.constructor(*self.constructor_args)
 | 
			
		||||
        input = self._get_input()
 | 
			
		||||
 | 
			
		||||
        if self.reference_fn is not None:
 | 
			
		||||
            out = test_case._forward(module, input)
 | 
			
		||||
            if isinstance(out, Variable):
 | 
			
		||||
                out = out.data
 | 
			
		||||
            ref_input = self._unpack_input(deepcopy(input))
 | 
			
		||||
            expected_out = self.reference_fn(ref_input, test_case._get_parameters(module)[0])
 | 
			
		||||
            test_case.assertEqual(out, expected_out)
 | 
			
		||||
 | 
			
		||||
        self.test_noncontig(test_case, module, input)
 | 
			
		||||
 | 
			
		||||
        # TODO: do this with in-memory files as soon as torch.save will support it
 | 
			
		||||
        with TemporaryFile() as f:
 | 
			
		||||
            test_case._forward(module, input)
 | 
			
		||||
            torch.save(module, f)
 | 
			
		||||
            f.seek(0)
 | 
			
		||||
            module_copy = torch.load(f)
 | 
			
		||||
            test_case.assertEqual(test_case._forward(module, input), test_case._forward(module_copy, input))
 | 
			
		||||
 | 
			
		||||
        self._do_test(test_case, module, input)
 | 
			
		||||
 | 
			
		||||
    def noncontiguize(self, obj):
 | 
			
		||||
        if isinstance(obj, list):
 | 
			
		||||
            return [self.noncontiguize(o) for o in obj]
 | 
			
		||||
        tensor = obj.data if isinstance(obj, Variable) else obj
 | 
			
		||||
        ndim = tensor.dim()
 | 
			
		||||
        noncontig = torch.stack([tensor.clone().zero_(), tensor], ndim).select(ndim, 1)
 | 
			
		||||
        assert noncontig.numel() == 1 or not noncontig.is_contiguous()
 | 
			
		||||
        if isinstance(obj, Variable):
 | 
			
		||||
            return Variable(noncontig, requires_grad=obj.requires_grad)
 | 
			
		||||
        return noncontig
 | 
			
		||||
 | 
			
		||||
    def test_noncontig(self, test_case, module, input):
 | 
			
		||||
        test_case._zero_grad_parameters(module)
 | 
			
		||||
        test_case._zero_grad_input(input)
 | 
			
		||||
        with freeze_rng_state():
 | 
			
		||||
            output = test_case._forward(module, input)
 | 
			
		||||
            grad_output = output
 | 
			
		||||
            if isinstance(grad_output, Variable):
 | 
			
		||||
                grad_output = grad_output.data.clone()
 | 
			
		||||
            else:
 | 
			
		||||
                grad_output = grad_output.clone()
 | 
			
		||||
                output = output.clone()
 | 
			
		||||
            grad_output.normal_()
 | 
			
		||||
            d_input = deepcopy(test_case._backward(module, input, output, grad_output))
 | 
			
		||||
            d_param = deepcopy(test_case._get_parameters(module)[1])
 | 
			
		||||
 | 
			
		||||
        nc_input = self.noncontiguize(input)
 | 
			
		||||
        nc_grad_output = self.noncontiguize(grad_output)
 | 
			
		||||
        for contig_i, contig_g in product((True, False), repeat=2):
 | 
			
		||||
            i = input if contig_i else nc_input
 | 
			
		||||
            go = grad_output if contig_g else nc_grad_output
 | 
			
		||||
            test_case._zero_grad_parameters(module)
 | 
			
		||||
            test_case._zero_grad_input(i)
 | 
			
		||||
            with freeze_rng_state():
 | 
			
		||||
                try:
 | 
			
		||||
                    out = test_case._forward(module, i)
 | 
			
		||||
                except Exception:
 | 
			
		||||
                    # Some modules will fail because of non contiguous inputs and we're ok with that
 | 
			
		||||
                    continue
 | 
			
		||||
                grad = test_case._backward(module, i, out, go)
 | 
			
		||||
 | 
			
		||||
                test_case.assertEqual(out, output)
 | 
			
		||||
                test_case.assertEqual(grad, d_input, 1e-4)
 | 
			
		||||
                test_case.assertEqual(test_case._get_parameters(module)[1], d_param)
 | 
			
		||||
 | 
			
		||||
    def test_cuda(self, test_case):
 | 
			
		||||
        if not TEST_CUDA or not self.should_test_cuda:
 | 
			
		||||
            raise unittest.SkipTest('Excluded from CUDA tests')
 | 
			
		||||
        try:
 | 
			
		||||
            cpu_input = self._get_input()
 | 
			
		||||
            type_map = {torch.DoubleTensor: torch.cuda.FloatTensor}
 | 
			
		||||
            gpu_input = to_gpu(cpu_input, type_map=type_map)
 | 
			
		||||
 | 
			
		||||
            cpu_module = self.constructor(*self.constructor_args)
 | 
			
		||||
            gpu_module = self.constructor(*self.constructor_args).float().cuda()
 | 
			
		||||
            cpu_param = test_case._get_parameters(cpu_module)
 | 
			
		||||
            gpu_param = test_case._get_parameters(gpu_module)
 | 
			
		||||
            for cpu_p, gpu_p in zip(cpu_param[0], gpu_param[0]):
 | 
			
		||||
                if isinstance(cpu_p, Variable):
 | 
			
		||||
                    cpu_p = cpu_p.data
 | 
			
		||||
                if isinstance(gpu_p, Variable):
 | 
			
		||||
                    gpu_p = gpu_p.data
 | 
			
		||||
                gpu_p.copy_(cpu_p)
 | 
			
		||||
 | 
			
		||||
            test_case._zero_grad_input(cpu_input)
 | 
			
		||||
            test_case._zero_grad_input(gpu_input)
 | 
			
		||||
            test_case._zero_grad_parameters(cpu_module)
 | 
			
		||||
            test_case._zero_grad_parameters(gpu_module)
 | 
			
		||||
            cpu_output = test_case._forward(cpu_module, cpu_input)
 | 
			
		||||
            gpu_output = test_case._forward(gpu_module, gpu_input)
 | 
			
		||||
            test_case.assertEqual(cpu_output, gpu_output, 2e-4)
 | 
			
		||||
 | 
			
		||||
            for i in range(5):
 | 
			
		||||
                cpu_output_t = cpu_output.data if isinstance(cpu_output, Variable) else cpu_output
 | 
			
		||||
                cpu_gradOutput = cpu_output_t.clone().bernoulli_()
 | 
			
		||||
                gpu_gradOutput = cpu_gradOutput.type('torch.cuda.FloatTensor')
 | 
			
		||||
                cpu_gradInput = test_case._backward(cpu_module, cpu_input, cpu_output, cpu_gradOutput)
 | 
			
		||||
                gpu_gradInput = test_case._backward(gpu_module, gpu_input, gpu_output, gpu_gradOutput)
 | 
			
		||||
                test_case.assertEqual(cpu_gradInput, gpu_gradInput, 2e-4)
 | 
			
		||||
                for cpu_d_p, gpu_d_p in zip(cpu_param[1], gpu_param[1]):
 | 
			
		||||
                    test_case.assertEqual(cpu_d_p, gpu_d_p, 2e-4)
 | 
			
		||||
 | 
			
		||||
            self.test_noncontig(test_case, gpu_module, gpu_input)
 | 
			
		||||
        except NotImplementedError:
 | 
			
		||||
            pass
 | 
			
		||||
        # TODO: remove this after CUDA scatter_ is implemented
 | 
			
		||||
        except AttributeError as e:
 | 
			
		||||
            if len(e.args) == 1 and "'FloatTensor' object has no attribute 'scatter_'" in e.args[0]:
 | 
			
		||||
                pass
 | 
			
		||||
            else:
 | 
			
		||||
                raise
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CriterionTest(TestBase):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(CriterionTest, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.target = self._get_target(kwargs['target'])
 | 
			
		||||
        self.should_test_cuda = kwargs.get('test_cuda', True)
 | 
			
		||||
 | 
			
		||||
    def _get_target(self, target):
 | 
			
		||||
        return target
 | 
			
		||||
 | 
			
		||||
    def __call__(self, test_case):
 | 
			
		||||
        module = self.constructor(*self.constructor_args)
 | 
			
		||||
        input = self._get_input()
 | 
			
		||||
 | 
			
		||||
        # Check that these methods don't raise errors
 | 
			
		||||
        module.__repr__()
 | 
			
		||||
        str(module)
 | 
			
		||||
 | 
			
		||||
        if self.reference_fn is not None:
 | 
			
		||||
            out = test_case._forward_criterion(module, input, self.target)
 | 
			
		||||
            target = self.target
 | 
			
		||||
            if isinstance(target, Variable):
 | 
			
		||||
                target = target.data
 | 
			
		||||
            expected_out = self.reference_fn(deepcopy(self._unpack_input(input)),
 | 
			
		||||
                                             deepcopy(target), module)
 | 
			
		||||
            test_case.assertEqual(out, expected_out)
 | 
			
		||||
 | 
			
		||||
        test_case.check_criterion_jacobian(module, input, self.target)
 | 
			
		||||
        self._do_extra_tests(test_case, module, input, self.target)
 | 
			
		||||
 | 
			
		||||
    def test_cuda(self, test_case):
 | 
			
		||||
        if not TEST_CUDA or not self.should_test_cuda:
 | 
			
		||||
            raise unittest.SkipTest('Excluded from CUDA tests')
 | 
			
		||||
        try:
 | 
			
		||||
            cpu_input = self._get_input()
 | 
			
		||||
            type_map = {
 | 
			
		||||
                torch.DoubleTensor: torch.cuda.FloatTensor,
 | 
			
		||||
            }
 | 
			
		||||
            gpu_input = to_gpu(cpu_input, type_map=type_map)
 | 
			
		||||
 | 
			
		||||
            cpu_target = self.target
 | 
			
		||||
            gpu_target = to_gpu(self.target, type_map=type_map)
 | 
			
		||||
 | 
			
		||||
            cpu_module = self.constructor(*self.constructor_args)
 | 
			
		||||
            gpu_module = self.constructor(*self.constructor_args).float().cuda()
 | 
			
		||||
 | 
			
		||||
            cpu_output = test_case._forward_criterion(cpu_module, cpu_input, cpu_target)
 | 
			
		||||
            gpu_output = test_case._forward_criterion(gpu_module, gpu_input, gpu_target)
 | 
			
		||||
            test_case.assertEqual(cpu_output, gpu_output, 4e-4)
 | 
			
		||||
 | 
			
		||||
            cpu_gradInput = test_case._backward_criterion(cpu_module, cpu_input, cpu_target)
 | 
			
		||||
            gpu_gradInput = test_case._backward_criterion(gpu_module, gpu_input, gpu_target)
 | 
			
		||||
            test_case.assertEqual(cpu_gradInput, gpu_gradInput, 4e-4)
 | 
			
		||||
        except NotImplementedError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def _do_extra_tests(self, test_case, module, input, target):
 | 
			
		||||
        pass
 | 
			
		||||
							
								
								
									
										8
									
								
								test/data/network1.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								test/data/network1.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
import torch.nn as nn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Net(nn.Module):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(Net, self).__init__()
 | 
			
		||||
        self.linear = nn.Linear(10, 20)
 | 
			
		||||
							
								
								
									
										9
									
								
								test/data/network2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								test/data/network2.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
import torch.nn as nn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Net(nn.Module):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(Net, self).__init__()
 | 
			
		||||
        self.linear = nn.Linear(10, 20)
 | 
			
		||||
        self.relu = nn.ReLU()
 | 
			
		||||
							
								
								
									
										71
									
								
								test/error_messages/storage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								test/error_messages/storage.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
import torch
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_error(desc, fn, *required_substrings):
 | 
			
		||||
    try:
 | 
			
		||||
        fn()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        error_message = e.args[0]
 | 
			
		||||
        print('=' * 80)
 | 
			
		||||
        print(desc)
 | 
			
		||||
        print('-' * 80)
 | 
			
		||||
        print(error_message)
 | 
			
		||||
        print('')
 | 
			
		||||
        for sub in required_substrings:
 | 
			
		||||
            assert sub in error_message
 | 
			
		||||
        return
 | 
			
		||||
    assert False, "given function ({}) didn't raise an error".format(desc)
 | 
			
		||||
 | 
			
		||||
check_error(
 | 
			
		||||
    'Wrong argument types',
 | 
			
		||||
    lambda: torch.FloatStorage(object()),
 | 
			
		||||
    'object')
 | 
			
		||||
 | 
			
		||||
check_error('Unknown keyword argument',
 | 
			
		||||
            lambda: torch.FloatStorage(content=1234.),
 | 
			
		||||
            'keyword')
 | 
			
		||||
 | 
			
		||||
check_error('Invalid types inside a sequence',
 | 
			
		||||
            lambda: torch.FloatStorage(['a', 'b']),
 | 
			
		||||
            'list', 'str')
 | 
			
		||||
 | 
			
		||||
check_error('Invalid size type',
 | 
			
		||||
            lambda: torch.FloatStorage(1.5),
 | 
			
		||||
            'float')
 | 
			
		||||
 | 
			
		||||
check_error('Invalid offset',
 | 
			
		||||
            lambda: torch.FloatStorage(torch.FloatStorage(2), 4),
 | 
			
		||||
            '2', '4')
 | 
			
		||||
 | 
			
		||||
check_error('Negative offset',
 | 
			
		||||
            lambda: torch.FloatStorage(torch.FloatStorage(2), -1),
 | 
			
		||||
            '2', '-1')
 | 
			
		||||
 | 
			
		||||
check_error('Invalid size',
 | 
			
		||||
            lambda: torch.FloatStorage(torch.FloatStorage(3), 1, 5),
 | 
			
		||||
            '2', '1', '5')
 | 
			
		||||
 | 
			
		||||
check_error('Negative size',
 | 
			
		||||
            lambda: torch.FloatStorage(torch.FloatStorage(3), 1, -5),
 | 
			
		||||
            '2', '1', '-5')
 | 
			
		||||
 | 
			
		||||
check_error('Invalid index type',
 | 
			
		||||
            lambda: torch.FloatStorage(10)['first item'],
 | 
			
		||||
            'str')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def assign():
 | 
			
		||||
    torch.FloatStorage(10)[1:-1] = '1'
 | 
			
		||||
check_error('Invalid value type',
 | 
			
		||||
            assign,
 | 
			
		||||
            'str')
 | 
			
		||||
 | 
			
		||||
check_error('resize_ with invalid type',
 | 
			
		||||
            lambda: torch.FloatStorage(10).resize_(1.5),
 | 
			
		||||
            'float')
 | 
			
		||||
 | 
			
		||||
check_error('fill_ with invalid type',
 | 
			
		||||
            lambda: torch.IntStorage(10).fill_('asdf'),
 | 
			
		||||
            'str')
 | 
			
		||||
 | 
			
		||||
# TODO: frombuffer
 | 
			
		||||
							
								
								
									
										6
									
								
								test/ffi/src/cpu/lib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								test/ffi/src/cpu/lib.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
 | 
			
		||||
void good_func(THFloatTensor *tensor, int a, float b);
 | 
			
		||||
void bad_func(THFloatTensor *tensor, int a, float b);
 | 
			
		||||
THFloatTensor * new_tensor(int a);
 | 
			
		||||
float int_to_float(int a);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								test/ffi/src/cpu/lib1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								test/ffi/src/cpu/lib1.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
#include <TH/TH.h>
 | 
			
		||||
 | 
			
		||||
void good_func(THFloatTensor *tensor, int a, float b)
 | 
			
		||||
{
 | 
			
		||||
  THFloatTensor_mul(tensor, tensor, a);
 | 
			
		||||
  THFloatTensor_add(tensor, tensor, b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
THFloatTensor * new_tensor(int a)
 | 
			
		||||
{
 | 
			
		||||
  THFloatTensor *t = THFloatTensor_newWithSize2d(a, a);
 | 
			
		||||
  THFloatTensor_fill(t, a);
 | 
			
		||||
  return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float int_to_float(int a)
 | 
			
		||||
{
 | 
			
		||||
  return a;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								test/ffi/src/cpu/lib2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								test/ffi/src/cpu/lib2.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
#include <TH/TH.h>
 | 
			
		||||
 | 
			
		||||
void bad_func(THFloatTensor *tensor, int a, float b)
 | 
			
		||||
{
 | 
			
		||||
  THFloatTensor_mul(tensor, tensor, a);
 | 
			
		||||
  THFloatTensor_add(tensor, tensor, b);
 | 
			
		||||
  THFloatTensor_addbmm(tensor, 1, tensor, 1, tensor, tensor);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								test/ffi/src/cuda/cudalib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								test/ffi/src/cuda/cudalib.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
#include <TH/TH.h>
 | 
			
		||||
#include <THC/THC.h>
 | 
			
		||||
 | 
			
		||||
extern THCState *state;
 | 
			
		||||
 | 
			
		||||
#include "../cpu/lib1.c"
 | 
			
		||||
 | 
			
		||||
void cuda_func(THCudaTensor *tensor, int a, float b)
 | 
			
		||||
{
 | 
			
		||||
  THCudaTensor_mul(state, tensor, tensor, a);
 | 
			
		||||
  THCudaTensor_add(state, tensor, tensor, b);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								test/ffi/src/cuda/cudalib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								test/ffi/src/cuda/cudalib.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
 | 
			
		||||
void good_func(THFloatTensor *tensor, int a, float b);
 | 
			
		||||
void cuda_func(THCudaTensor *tensor, int a, float b);
 | 
			
		||||
THFloatTensor * new_tensor(int a);
 | 
			
		||||
float int_to_float(int a);
 | 
			
		||||
							
								
								
									
										5
									
								
								test/ffi/src/lib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								test/ffi/src/lib.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
 | 
			
		||||
void my_func(THFloatTensor *tensor, int a, float b);
 | 
			
		||||
void my_cuda_func(THCudaTensor *tensor, int a, float b);
 | 
			
		||||
THFloatTensor * new_t(int a);
 | 
			
		||||
float new_int(int a);
 | 
			
		||||
							
								
								
									
										14
									
								
								test/optim/compare.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								test/optim/compare.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
 | 
			
		||||
th test.lua > lua.out
 | 
			
		||||
python3 test.py > python.out
 | 
			
		||||
 | 
			
		||||
diff lua.out python.out >/dev/null 2>&1
 | 
			
		||||
RESULT=$?
 | 
			
		||||
if [[ RESULT -eq 0 ]]; then
 | 
			
		||||
    echo "PASS"
 | 
			
		||||
else
 | 
			
		||||
    echo "FAIL"
 | 
			
		||||
    echo "Press ENTER to open vimdiff"
 | 
			
		||||
    read
 | 
			
		||||
    vimdiff lua.out python.out
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										33
									
								
								test/optim/test.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								test/optim/test.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
local cjson = require 'cjson'
 | 
			
		||||
require 'optim'
 | 
			
		||||
 | 
			
		||||
function rosenbrock(t)
 | 
			
		||||
    x, y = t[1], t[2]
 | 
			
		||||
    return (1 - x) ^ 2 + 100 * (y - x^2)^2
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function drosenbrock(t)
 | 
			
		||||
    x, y = t[1], t[2]
 | 
			
		||||
    return torch.DoubleTensor({-400 * x * (y - x^2) - 2 * (1 - x), 200 * x * (y - x^2)})
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local fd = io.open('tests.json', 'r')
 | 
			
		||||
local tests = cjson.decode(fd:read('*a'))
 | 
			
		||||
fd:close()
 | 
			
		||||
 | 
			
		||||
for i, test in ipairs(tests) do
 | 
			
		||||
    print(test.algorithm)
 | 
			
		||||
    algorithm = optim[test.algorithm]
 | 
			
		||||
    for i, config in ipairs(test.config) do
 | 
			
		||||
        print('================================================================================')
 | 
			
		||||
        params = torch.DoubleTensor({1.5, 1.5})
 | 
			
		||||
        for i = 1, 100 do
 | 
			
		||||
            function closure(x)
 | 
			
		||||
                return rosenbrock(x), drosenbrock(x)
 | 
			
		||||
            end
 | 
			
		||||
            algorithm(closure, params, config)
 | 
			
		||||
            print(string.format('%.8f\t%.8f', params[1], params[2]))
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										41
									
								
								test/optim/test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								test/optim/test.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
import json
 | 
			
		||||
import torch
 | 
			
		||||
import torch.legacy.optim as optim
 | 
			
		||||
from pprint import pprint
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rosenbrock(tensor):
 | 
			
		||||
    x, y = tensor
 | 
			
		||||
    return (1 - x) ** 2 + 100 * (y - x ** 2) ** 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def drosenbrock(tensor):
 | 
			
		||||
    x, y = tensor
 | 
			
		||||
    return torch.DoubleTensor((-400 * x * (y - x ** 2) - 2 * (1 - x), 200 * x * (y - x ** 2)))
 | 
			
		||||
 | 
			
		||||
algorithms = {
 | 
			
		||||
    'adadelta': optim.adadelta,
 | 
			
		||||
    'adagrad': optim.adagrad,
 | 
			
		||||
    'adam': optim.adam,
 | 
			
		||||
    'adamax': optim.adamax,
 | 
			
		||||
    'asgd': optim.asgd,
 | 
			
		||||
    'cg': optim.cg,
 | 
			
		||||
    'nag': optim.nag,
 | 
			
		||||
    'rmsprop': optim.rmsprop,
 | 
			
		||||
    'rprop': optim.rprop,
 | 
			
		||||
    'sgd': optim.sgd,
 | 
			
		||||
    'lbfgs': optim.lbfgs,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
with open('tests.json', 'r') as f:
 | 
			
		||||
    tests = json.loads(f.read())
 | 
			
		||||
 | 
			
		||||
for test in tests:
 | 
			
		||||
    print(test['algorithm'] + '\t')
 | 
			
		||||
    algorithm = algorithms[test['algorithm']]
 | 
			
		||||
    for config in test['config']:
 | 
			
		||||
        print('================================================================================\t')
 | 
			
		||||
        params = torch.DoubleTensor((1.5, 1.5))
 | 
			
		||||
        for i in range(100):
 | 
			
		||||
            algorithm(lambda x: (rosenbrock(x), drosenbrock(x)), params, config)
 | 
			
		||||
            print('{:.8f}\t{:.8f}\t'.format(params[0], params[1]))
 | 
			
		||||
							
								
								
									
										109
									
								
								test/optim/tests.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								test/optim/tests.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "adadelta",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"rho": 0.95},
 | 
			
		||||
            {"rho": 0.95, "eps": 1e-3},
 | 
			
		||||
            {"weightDecay": 0.2}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "adagrad",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "adam",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"learningRate": 1e-4},
 | 
			
		||||
            {"learningRate": 1e-4, "beta1": 0.92},
 | 
			
		||||
            {"learningRate": 1e-4, "beta1": 0.92, "beta2": 0.96},
 | 
			
		||||
            {"learningRate": 1e-4, "beta1": 0.92, "beta2": 0.96, "epsilon": 1e-3},
 | 
			
		||||
            {"learningRate": 1e-4, "weightDecay": 0.1}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "adamax",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"learningRate": 1e-4},
 | 
			
		||||
            {"learningRate": 1e-4, "beta1": 0.92},
 | 
			
		||||
            {"learningRate": 1e-4, "beta1": 0.92, "beta2": 0.96},
 | 
			
		||||
            {"learningRate": 1e-4, "beta1": 0.92, "beta2": 0.96, "epsilon": 1e-3}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "asgd",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"eta0": 1e-4},
 | 
			
		||||
            {"eta0": 1e-4, "lambda": 1e-2},
 | 
			
		||||
            {"eta0": 1e-4, "lambda": 1e-2, "alpha": 0.9},
 | 
			
		||||
            {"eta0": 1e-4, "lambda": 1e-2, "alpha": 0.9, "t0": 10}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "cg",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"rho": 0.02},
 | 
			
		||||
            {"sig": 0.06},
 | 
			
		||||
            {"int": 0.12},
 | 
			
		||||
            {"ext": 3.2},
 | 
			
		||||
            {"maxIter": 5},
 | 
			
		||||
            {"ratio": 95}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "nag",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"learningRate": 1e-4},
 | 
			
		||||
            {"learningRate": 1e-4, "learningRateDecay": 0.1},
 | 
			
		||||
            {"learningRate": 1e-4, "weightDecay": 0.3},
 | 
			
		||||
            {"learningRate": 1e-4, "momentum": 0.95},
 | 
			
		||||
            {"learningRate": 1e-4, "momentum": 0.95, "dampening": 0.8}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "rmsprop",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"learningRate": 1e-4},
 | 
			
		||||
            {"learningRate": 1e-4, "alpha": 0.95},
 | 
			
		||||
            {"learningRate": 1e-4, "alpha": 0.95, "epsilon": 1e-3},
 | 
			
		||||
            {"weightDecay": 0.2}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "rprop",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"stepsize": 0.05},
 | 
			
		||||
            {"stepsize": 0.05, "etaplus": 1.15},
 | 
			
		||||
            {"stepsize": 0.05, "etaplus": 1.15, "etaminus": 0.6},
 | 
			
		||||
            {"stepsize": 0.05, "etaplus": 1.15, "etaminus": 0.6, "stepsizemax": 1, "stepsizemin": 1e-3},
 | 
			
		||||
            {"stepsize": 0.05, "etaplus": 1.15, "etaminus": 0.6, "niter": 10}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "sgd",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"learningRate": 1e-4},
 | 
			
		||||
            {"learningRate": 1e-4, "momentum": 0.95, "dampening": 0.9},
 | 
			
		||||
            {"learningRate": 1e-4, "nesterov": true, "momentum": 0.95, "dampening": 0},
 | 
			
		||||
            {"weightDecay": 0.2}
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "algorithm": "lbfgs",
 | 
			
		||||
        "config": [
 | 
			
		||||
            {},
 | 
			
		||||
            {"learningRate": 1e-1}
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										95
									
								
								test/run_test.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										95
									
								
								test/run_test.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,95 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
PYCMD=${PYCMD:="python"}
 | 
			
		||||
COVERAGE=0
 | 
			
		||||
while [[ "$#" -gt 0 ]]; do
 | 
			
		||||
    case "$1" in
 | 
			
		||||
        -p|--python) PYCMD=$2; shift 2 ;;
 | 
			
		||||
        -c|--coverage) COVERAGE=1; shift 1;;
 | 
			
		||||
        --) shift; break ;;
 | 
			
		||||
        *) echo "Invalid argument: $1!" ; exit 1 ;;
 | 
			
		||||
    esac
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
if [[ $COVERAGE -eq 1 ]]; then
 | 
			
		||||
    coverage erase
 | 
			
		||||
    PYCMD="coverage run --parallel-mode --source torch "
 | 
			
		||||
    echo "coverage flag found. Setting python command to: \"$PYCMD\""
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
pushd "$(dirname "$0")"
 | 
			
		||||
 | 
			
		||||
echo "Running torch tests"
 | 
			
		||||
$PYCMD test_torch.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running autograd tests"
 | 
			
		||||
$PYCMD test_autograd.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running sparse tests"
 | 
			
		||||
$PYCMD test_sparse.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running nn tests"
 | 
			
		||||
$PYCMD test_nn.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running legacy nn tests"
 | 
			
		||||
$PYCMD test_legacy_nn.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running optim tests"
 | 
			
		||||
$PYCMD test_optim.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running multiprocessing tests"
 | 
			
		||||
$PYCMD test_multiprocessing.py $@
 | 
			
		||||
MULTIPROCESSING_METHOD=spawn $PYCMD test_multiprocessing.py $@
 | 
			
		||||
MULTIPROCESSING_METHOD=forkserver $PYCMD test_multiprocessing.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running util tests"
 | 
			
		||||
$PYCMD test_utils.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running dataloader tests"
 | 
			
		||||
$PYCMD test_dataloader.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running cuda tests"
 | 
			
		||||
$PYCMD test_cuda.py $@
 | 
			
		||||
 | 
			
		||||
echo "Running NCCL tests"
 | 
			
		||||
$PYCMD test_nccl.py $@
 | 
			
		||||
 | 
			
		||||
distributed_set_up() {
 | 
			
		||||
  export TEMP_DIR="$(mktemp -d)"
 | 
			
		||||
  rm -rf "$TEMP_DIR/"*
 | 
			
		||||
  mkdir "$TEMP_DIR/barrier"
 | 
			
		||||
  mkdir "$TEMP_DIR/test_dir"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
distributed_tear_down() {
 | 
			
		||||
  rm -rf "$TEMP_DIR"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
trap distributed_tear_down EXIT SIGHUP SIGINT SIGTERM
 | 
			
		||||
 | 
			
		||||
echo "Running distributed tests for the TCP backend"
 | 
			
		||||
distributed_set_up
 | 
			
		||||
BACKEND=tcp WORLD_SIZE=3 $PYCMD ./test_distributed.py
 | 
			
		||||
distributed_tear_down
 | 
			
		||||
 | 
			
		||||
echo "Running distributed tests for the Gloo backend"
 | 
			
		||||
distributed_set_up
 | 
			
		||||
BACKEND=gloo WORLD_SIZE=3 $PYCMD ./test_distributed.py
 | 
			
		||||
distributed_tear_down
 | 
			
		||||
 | 
			
		||||
if [ -x "$(command -v mpiexec)" ]; then
 | 
			
		||||
  echo "Running distributed tests for the MPI backend"
 | 
			
		||||
  distributed_set_up
 | 
			
		||||
  BACKEND=mpi mpiexec -n 3 $PYCMD ./test_distributed.py
 | 
			
		||||
  distributed_tear_down
 | 
			
		||||
else
 | 
			
		||||
  echo "Skipping MPI backend tests (MPI not found)"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ $COVERAGE -eq 1 ]]; then
 | 
			
		||||
    coverage combine
 | 
			
		||||
    coverage html
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
popd
 | 
			
		||||
							
								
								
									
										2307
									
								
								test/test_autograd.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2307
									
								
								test/test_autograd.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										936
									
								
								test/test_cuda.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										936
									
								
								test/test_cuda.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,936 @@
 | 
			
		||||
import math
 | 
			
		||||
import tempfile
 | 
			
		||||
import unittest
 | 
			
		||||
from itertools import repeat
 | 
			
		||||
 | 
			
		||||
import torch
 | 
			
		||||
import torch.cuda
 | 
			
		||||
import torch.cuda.comm as comm
 | 
			
		||||
 | 
			
		||||
from test_torch import TestTorch
 | 
			
		||||
from common import TestCase, get_gpu_type, to_gpu, freeze_rng_state, run_tests
 | 
			
		||||
 | 
			
		||||
HAS_CUDA = True
 | 
			
		||||
if not torch.cuda.is_available():
 | 
			
		||||
    print('CUDA not available, skipping tests')
 | 
			
		||||
    TestCase = object  # noqa: F811
 | 
			
		||||
    HAS_CUDA = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_floating(t):
 | 
			
		||||
    return type(t) in [torch.FloatTensor, torch.DoubleTensor,
 | 
			
		||||
                       torch.cuda.FloatTensor, torch.cuda.DoubleTensor]
 | 
			
		||||
 | 
			
		||||
types = [
 | 
			
		||||
    torch.FloatTensor,
 | 
			
		||||
    torch.DoubleTensor,
 | 
			
		||||
    torch.LongTensor,
 | 
			
		||||
    torch.IntTensor,
 | 
			
		||||
    torch.ShortTensor,
 | 
			
		||||
    torch.CharTensor,
 | 
			
		||||
    torch.ByteTensor,
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
float_types = [
 | 
			
		||||
    torch.FloatTensor,
 | 
			
		||||
    torch.DoubleTensor
 | 
			
		||||
]  # TODO: add half...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def number(floating, integer, t):
 | 
			
		||||
    name = type(t).__name__
 | 
			
		||||
    if 'Double' in name or 'Float' in name or 'Half' in name:
 | 
			
		||||
        return floating
 | 
			
		||||
    else:
 | 
			
		||||
        return integer
 | 
			
		||||
# TODO: check HalfTensor
 | 
			
		||||
 | 
			
		||||
S = 10
 | 
			
		||||
M = 50
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def make_tensor(t, *sizes):
 | 
			
		||||
    return t(*sizes).copy_(torch.randn(*sizes))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_2d(t):
 | 
			
		||||
    return make_tensor(t, S, S)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_2d_scaled(t, scale=10):
 | 
			
		||||
    return make_tensor(t, S, S).mul(scale)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_2d_oneish(t):
 | 
			
		||||
    if is_floating(t):
 | 
			
		||||
        return make_tensor(t, S, S).clamp(min=0.99, max=1.01)
 | 
			
		||||
    else:
 | 
			
		||||
        return t(S, S).fill_(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_3d(t):
 | 
			
		||||
    return make_tensor(t, S, S, S)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def medium_1d(t):
 | 
			
		||||
    return make_tensor(t, M)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def medium_2d(t):
 | 
			
		||||
    return make_tensor(t, M, M)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def medium_2d_scaled(t, scale=10):
 | 
			
		||||
    return make_tensor(t, M, M).mul(scale)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_3d_ones(t):
 | 
			
		||||
    return t(S, S, S).copy_(torch.ones(S, S, S))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_3d_positive(t):
 | 
			
		||||
    min_val = 1e-3 if is_floating(t) else 2
 | 
			
		||||
    return make_tensor(t, S, S, S).clamp_(min_val, 120)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_3d_unique(t):
 | 
			
		||||
    return t(S, S, S).copy_(torch.arange(1, S * S * S + 1).view(S, S, S))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_1d_lapack(t):
 | 
			
		||||
    return t(1, 3).copy_(torch.arange(1, 4).view(3))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_2d_lapack(t):
 | 
			
		||||
    return t(3, 3).copy_(torch.arange(1, 10).view(3, 3))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_2d_lapack_skinny(t):
 | 
			
		||||
    return t(3, 4).copy_(torch.arange(1, 13).view(3, 4))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def small_2d_lapack_fat(t):
 | 
			
		||||
    return t(4, 3).copy_(torch.arange(1, 13).view(4, 3))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def large_2d_lapack(t):
 | 
			
		||||
    return t(1000, 1000).normal_()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def new_t(*sizes):
 | 
			
		||||
    def tmp(t):
 | 
			
		||||
        return t(*sizes).copy_(torch.randn(*sizes))
 | 
			
		||||
    return tmp
 | 
			
		||||
 | 
			
		||||
tests = [
 | 
			
		||||
    ('add', small_3d, lambda t: [number(3.14, 3, t)]),
 | 
			
		||||
    ('add', small_3d, lambda t: [small_3d_positive(t)], 'tensor'),
 | 
			
		||||
    ('add', small_3d, lambda t: [number(0.2, 2, t), small_3d_positive(t)], 'scalar_tensor'),
 | 
			
		||||
    ('sub', small_3d, lambda t: [number(3.14, 3, t)],),
 | 
			
		||||
    ('sub', small_3d, lambda t: [small_3d_positive(t)], 'tensor'),
 | 
			
		||||
    ('mul', small_3d, lambda t: [number(3.14, 3, t)],),
 | 
			
		||||
    ('mul', small_3d, lambda t: [small_3d_positive(t)], 'tensor'),
 | 
			
		||||
    ('div', small_3d, lambda t: [number(3.14, 3, t)],),
 | 
			
		||||
    ('div', small_3d, lambda t: [small_3d_positive(t)], 'tensor'),
 | 
			
		||||
    ('pow', small_3d, lambda t: [number(3.14, 3, t)], None, float_types),
 | 
			
		||||
    ('pow', small_3d, lambda t: [small_3d(t).abs_()], 'tensor', float_types),
 | 
			
		||||
    ('addbmm', small_2d, lambda t: [small_3d(t), small_3d(t)], None, float_types),
 | 
			
		||||
    ('addbmm', small_2d, lambda t: [number(0.4, 2, t), small_3d(t), small_3d(t)], 'scalar'),
 | 
			
		||||
    ('addbmm', small_2d, lambda t: [number(0.5, 3, t), number(0.4, 2, t), small_3d(t), small_3d(t)], 'two_scalars'),
 | 
			
		||||
    ('baddbmm', small_3d, lambda t: [small_3d(t), small_3d(t)],),
 | 
			
		||||
    ('baddbmm', small_3d, lambda t: [number(0.4, 2, t), small_3d(t), small_3d(t)], 'scalar'),
 | 
			
		||||
    ('baddbmm', small_3d, lambda t: [number(0.5, 3, t), number(0.4, 2, t), small_3d(t), small_3d(t)], 'two_scalars'),
 | 
			
		||||
    ('addcdiv', small_2d_lapack, lambda t: [small_2d_lapack(t).mul(2), small_2d_lapack(t)],),
 | 
			
		||||
    ('addcdiv', small_2d_lapack, lambda t: [number(2.8, 1, t),
 | 
			
		||||
                                            small_2d_lapack(t).mul(2), small_2d_lapack(t)], 'scalar'),
 | 
			
		||||
    ('addcmul', small_3d, lambda t: [small_3d(t), small_3d(t)],),
 | 
			
		||||
    ('addcmul', small_3d, lambda t: [number(0.4, 2, t), small_3d(t), small_3d(t)], 'scalar'),
 | 
			
		||||
    ('addmm', medium_2d, lambda t: [medium_2d(t), medium_2d(t)],),
 | 
			
		||||
    ('addmm', medium_2d, lambda t: [number(0.4, 2, t), medium_2d(t), medium_2d(t)], 'scalar'),
 | 
			
		||||
    ('addmm', medium_2d, lambda t: [number(0.5, 3, t), number(0.4, 2, t), medium_2d(t), medium_2d(t)], 'two_scalars'),
 | 
			
		||||
    ('addmv', medium_1d, lambda t: [medium_2d(t), medium_1d(t)],),
 | 
			
		||||
    ('addmv', medium_1d, lambda t: [number(0.4, 2, t), medium_2d(t), medium_1d(t)], 'scalar'),
 | 
			
		||||
    ('addmv', medium_1d, lambda t: [number(0.5, 3, t), number(0.4, 2, t), medium_2d(t), medium_1d(t)], 'two_scalars'),
 | 
			
		||||
    ('addr', medium_2d, lambda t: [medium_1d(t), medium_1d(t)],),
 | 
			
		||||
    ('addr', medium_2d, lambda t: [number(0.4, 2, t), medium_1d(t), medium_1d(t)], 'scalar'),
 | 
			
		||||
    ('addr', medium_2d, lambda t: [number(0.5, 3, t), number(0.4, 2, t), medium_1d(t), medium_1d(t)], 'two_scalars'),
 | 
			
		||||
    ('atan2', medium_2d, lambda t: [medium_2d(t)], None, float_types),
 | 
			
		||||
    ('fmod', small_3d, lambda t: [3], 'value'),
 | 
			
		||||
    ('fmod', small_3d, lambda t: [small_3d_positive(t)], 'tensor'),
 | 
			
		||||
    ('chunk', medium_2d, lambda t: [4],),
 | 
			
		||||
    ('chunk', medium_2d, lambda t: [4, 1], 'dim'),
 | 
			
		||||
    ('chunk', medium_2d, lambda t: [4, -2], 'neg_dim'),
 | 
			
		||||
    ('clamp', medium_2d_scaled, lambda t: [-1, 5],),
 | 
			
		||||
    ('clone', medium_2d, lambda t: [],),
 | 
			
		||||
    ('contiguous', medium_2d, lambda t: [],),
 | 
			
		||||
    ('cross', new_t(M, 3, M), lambda t: [new_t(M, 3, M)(t)],),
 | 
			
		||||
    ('cumprod', small_3d, lambda t: [1],),
 | 
			
		||||
    ('cumprod', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('cumsum', small_3d, lambda t: [1],),
 | 
			
		||||
    ('cumsum', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('dim', small_3d, lambda t: [],),
 | 
			
		||||
    ('dist', small_2d, lambda t: [small_2d(t)],),
 | 
			
		||||
    ('dist', small_2d, lambda t: [small_2d(t), 3], '3_norm'),
 | 
			
		||||
    ('dist', small_2d, lambda t: [small_2d(t), 2.5], '2_5_norm'),
 | 
			
		||||
    ('dot', medium_1d, lambda t: [medium_1d(t)],),
 | 
			
		||||
    ('element_size', medium_1d, lambda t: [],),
 | 
			
		||||
    ('eq', small_3d_ones, lambda t: [small_3d(t)],),
 | 
			
		||||
    ('eq', small_3d_ones, lambda t: [small_3d_ones(t)], 'equal'),
 | 
			
		||||
    ('ne', small_3d_ones, lambda t: [small_3d(t)],),
 | 
			
		||||
    ('ne', small_3d_ones, lambda t: [small_3d_ones(t)], 'equal'),
 | 
			
		||||
    ('equal', small_3d_ones, lambda t: [small_3d_ones(t)], 'equal'),
 | 
			
		||||
    ('equal', small_3d_ones, lambda t: [small_3d(t)],),
 | 
			
		||||
    ('expand', new_t(M, 1, M), lambda t: [M, 4, M],),
 | 
			
		||||
    ('expand_as', new_t(M, 1, M), lambda t: [new_t(M, 4, M)(t)],),
 | 
			
		||||
    ('fill', medium_2d, lambda t: [number(3.14, 3, t)],),
 | 
			
		||||
    ('ge', medium_2d, lambda t: [medium_2d(t)],),
 | 
			
		||||
    ('le', medium_2d, lambda t: [medium_2d(t)],),
 | 
			
		||||
    ('gt', medium_2d, lambda t: [medium_2d(t)],),
 | 
			
		||||
    ('lt', medium_2d, lambda t: [medium_2d(t)],),
 | 
			
		||||
    ('is_contiguous', medium_2d, lambda t: [],),
 | 
			
		||||
    # TODO: can't check negative case - GPU copy will be contiguous
 | 
			
		||||
    ('is_same_size', medium_2d, lambda t: [small_3d(t)], 'negative'),
 | 
			
		||||
    ('is_same_size', medium_2d, lambda t: [medium_2d(t)], 'positive'),
 | 
			
		||||
    ('is_set_to', medium_2d, lambda t: [medium_2d(t)],),
 | 
			
		||||
    # TODO: positive case
 | 
			
		||||
    ('kthvalue', small_3d_unique, lambda t: [3],),
 | 
			
		||||
    ('kthvalue', small_3d_unique, lambda t: [3, 1], 'dim'),
 | 
			
		||||
    ('kthvalue', small_3d_unique, lambda t: [3, -1], 'neg_dim'),
 | 
			
		||||
    ('lerp', small_3d, lambda t: [small_3d(t), 0.3],),
 | 
			
		||||
    ('max', small_3d_unique, lambda t: [],),
 | 
			
		||||
    ('max', small_3d_unique, lambda t: [1], 'dim'),
 | 
			
		||||
    ('max', small_3d_unique, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('max', medium_2d, lambda t: [medium_2d(t)], 'elementwise'),
 | 
			
		||||
    ('min', small_3d_unique, lambda t: [],),
 | 
			
		||||
    ('min', small_3d_unique, lambda t: [1], 'dim'),
 | 
			
		||||
    ('min', small_3d_unique, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('min', medium_2d, lambda t: [medium_2d(t)], 'elementwise'),
 | 
			
		||||
    ('mean', small_3d, lambda t: [],),
 | 
			
		||||
    ('mean', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('mean', small_3d, lambda t: [1], 'dim'),
 | 
			
		||||
    ('mode', small_3d, lambda t: [],),
 | 
			
		||||
    ('mode', small_3d, lambda t: [1], 'dim'),
 | 
			
		||||
    ('mode', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('remainder', small_3d, lambda t: [3], 'value'),
 | 
			
		||||
    ('remainder', small_3d, lambda t: [-3], 'negative_value'),
 | 
			
		||||
    ('remainder', small_3d, lambda t: [small_3d_positive(t)], 'tensor'),
 | 
			
		||||
    ('remainder', small_3d, lambda t: [0 - small_3d_positive(t)], 'negative_tensor'),
 | 
			
		||||
    ('std', small_3d, lambda t: [],),
 | 
			
		||||
    ('std', small_3d, lambda t: [1], 'dim'),
 | 
			
		||||
    ('std', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('var', small_3d, lambda t: [],),
 | 
			
		||||
    ('var', small_3d, lambda t: [1], 'dim'),
 | 
			
		||||
    ('var', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('ndimension', small_3d, lambda t: [],),
 | 
			
		||||
    ('nelement', small_3d, lambda t: [],),
 | 
			
		||||
    ('numel', small_3d, lambda t: [],),
 | 
			
		||||
    ('narrow', small_3d, lambda t: [1, 3, 2],),
 | 
			
		||||
    ('narrow', small_3d, lambda t: [-1, 3, 2], 'neg_dim'),
 | 
			
		||||
    ('nonzero', small_3d, lambda t: [],),
 | 
			
		||||
    ('norm', small_3d, lambda t: [],),
 | 
			
		||||
    ('norm', small_3d, lambda t: [3], '3_norm'),
 | 
			
		||||
    ('norm', small_3d, lambda t: [3, 0], '3_norm_dim'),
 | 
			
		||||
    ('norm', small_3d, lambda t: [3, -2], '3_norm_neg_dim'),
 | 
			
		||||
    ('ones', small_3d, lambda t: [1, 2, 3, 4, 5],),
 | 
			
		||||
    ('permute', new_t(1, 2, 3, 4), lambda t: [2, 1, 3, 0],),
 | 
			
		||||
    ('prod', small_2d_oneish, lambda t: [],),
 | 
			
		||||
    ('prod', small_3d, lambda t: [1], 'dim'),
 | 
			
		||||
    ('prod', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('sum', small_2d, lambda t: [],),
 | 
			
		||||
    ('sum', small_3d, lambda t: [1], 'dim'),
 | 
			
		||||
    ('sum', small_3d, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('renorm', small_3d, lambda t: [2, 1, 1], '2_norm'),
 | 
			
		||||
    ('renorm', small_3d, lambda t: [2, -1, 1], '2_norm_neg_dim'),
 | 
			
		||||
    ('renorm', small_3d, lambda t: [1.5, 1, 1], '1_5_norm'),
 | 
			
		||||
    ('repeat', small_2d, lambda t: [2, 2, 2],),
 | 
			
		||||
    ('size', new_t(1, 2, 3, 4), lambda t: [],),
 | 
			
		||||
    ('size', new_t(1, 2, 3, 4), lambda t: [1], 'dim'),
 | 
			
		||||
    ('size', new_t(1, 2, 3, 4), lambda t: [-2], 'neg_dim'),
 | 
			
		||||
    ('sort', small_3d_unique, lambda t: [],),
 | 
			
		||||
    ('sort', small_3d_unique, lambda t: [1], 'dim'),
 | 
			
		||||
    ('sort', small_3d_unique, lambda t: [-1], 'neg_dim'),
 | 
			
		||||
    ('sort', small_3d_unique, lambda t: [1, True], 'dim_descending'),
 | 
			
		||||
    ('sort', small_3d_unique, lambda t: [-1, True], 'neg_dim_descending'),
 | 
			
		||||
    ('split', small_3d, lambda t: [2],),
 | 
			
		||||
    ('split', small_3d, lambda t: [2, 1], 'dim'),
 | 
			
		||||
    ('split', small_3d, lambda t: [2, -3], 'neg_dim'),
 | 
			
		||||
    ('squeeze', new_t(1, 2, 1, 4), lambda t: [],),
 | 
			
		||||
    ('squeeze', new_t(1, 2, 1, 4), lambda t: [2], 'dim'),
 | 
			
		||||
    ('squeeze', new_t(1, 2, 1, 4), lambda t: [-2], 'neg_dim'),
 | 
			
		||||
    ('t', new_t(1, 2), lambda t: [],),
 | 
			
		||||
    ('transpose', new_t(1, 2, 3, 4), lambda t: [1, 2],),
 | 
			
		||||
    ('transpose', new_t(1, 2, 3, 4), lambda t: [-1, -2], 'neg_dim'),
 | 
			
		||||
    ('to_list', small_3d, lambda t: [],),
 | 
			
		||||
    ('topk', small_3d_unique, lambda t: [2, 1, False, True], 'dim_sort'),
 | 
			
		||||
    ('topk', small_3d_unique, lambda t: [2, -1, False, True], 'neg_dim_sort'),
 | 
			
		||||
    ('topk', small_3d_unique, lambda t: [2, 1, True, True], 'dim_desc_sort'),
 | 
			
		||||
    ('trace', medium_2d, lambda t: [],),
 | 
			
		||||
    ('tril', medium_2d, lambda t: [],),
 | 
			
		||||
    ('tril', medium_2d, lambda t: [2], 'positive'),
 | 
			
		||||
    ('tril', medium_2d, lambda t: [-2], 'negative'),
 | 
			
		||||
    ('triu', medium_2d, lambda t: [],),
 | 
			
		||||
    ('triu', medium_2d, lambda t: [2], 'positive'),
 | 
			
		||||
    ('triu', medium_2d, lambda t: [-2], 'negative'),
 | 
			
		||||
    ('unsqueeze', new_t(2, 3, 4), lambda t: [2],),
 | 
			
		||||
    ('unsqueeze', new_t(2, 3, 4), lambda t: [-2], 'neg_dim'),
 | 
			
		||||
    ('view', small_3d, lambda t: [100, 10],),
 | 
			
		||||
    ('view_as', small_3d, lambda t: [t(100, 10)],),
 | 
			
		||||
    ('zero', small_3d, lambda t: [],),
 | 
			
		||||
    ('zeros', small_3d, lambda t: [1, 2, 3, 4],),
 | 
			
		||||
    ('rsqrt', lambda t: small_3d(t) + 1, lambda t: [], None, float_types),
 | 
			
		||||
    ('sinh', lambda t: small_3d(t).clamp(-1, 1), lambda t: [], None, float_types),
 | 
			
		||||
    ('tan', lambda t: small_3d(t).clamp(-1, 1), lambda t: [], None, float_types),
 | 
			
		||||
    # lapack tests
 | 
			
		||||
    ('qr', small_2d_lapack, lambda t: [], 'square', float_types),
 | 
			
		||||
    ('qr', small_2d_lapack_skinny, lambda t: [], 'skinny', float_types),
 | 
			
		||||
    ('qr', small_2d_lapack_fat, lambda t: [], 'fat', float_types),
 | 
			
		||||
    ('qr', large_2d_lapack, lambda t: [], 'big', float_types),
 | 
			
		||||
    ('inverse', new_t(20, 20), lambda t: [], None, float_types),
 | 
			
		||||
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# TODO: random functions, cat, gather, scatter, index*, masked*,
 | 
			
		||||
#       resize, resizeAs, storage_offset, storage, stride, unfold
 | 
			
		||||
 | 
			
		||||
custom_precision = {
 | 
			
		||||
    'addbmm': 1e-4,
 | 
			
		||||
    'addmm': 1e-4,
 | 
			
		||||
    'addmv': 1e-4,
 | 
			
		||||
    'addr': 1e-4,
 | 
			
		||||
    'baddbmm': 1e-4,
 | 
			
		||||
    'rsqrt': 1e-4,
 | 
			
		||||
    'cumprod': 1e-4,
 | 
			
		||||
    'qr': 3e-4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
simple_pointwise = [
 | 
			
		||||
    'abs',
 | 
			
		||||
    'sign',
 | 
			
		||||
]
 | 
			
		||||
for fn in simple_pointwise:
 | 
			
		||||
    tests.append((fn, small_3d, lambda t: []))
 | 
			
		||||
 | 
			
		||||
simple_pointwise_float = [
 | 
			
		||||
    'log',
 | 
			
		||||
    'log1p',
 | 
			
		||||
    'sigmoid',
 | 
			
		||||
    'sin',
 | 
			
		||||
    'sqrt',
 | 
			
		||||
    'tanh',
 | 
			
		||||
    'acos',
 | 
			
		||||
    'asin',
 | 
			
		||||
    'atan',
 | 
			
		||||
    'cos',
 | 
			
		||||
    'cosh',
 | 
			
		||||
    'exp',
 | 
			
		||||
    'reciprocal',
 | 
			
		||||
    'floor',
 | 
			
		||||
    'frac',
 | 
			
		||||
    'neg',
 | 
			
		||||
    'round',
 | 
			
		||||
    'trunc',
 | 
			
		||||
    'ceil',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
for fn in simple_pointwise_float:
 | 
			
		||||
    tests.append((fn, small_3d, lambda t: [], None, float_types))
 | 
			
		||||
 | 
			
		||||
_cycles_per_ms = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_cycles_per_ms():
 | 
			
		||||
    """Approximate number of cycles per millisecond for torch.cuda._sleep"""
 | 
			
		||||
    global _cycles_per_ms
 | 
			
		||||
    if _cycles_per_ms is None:
 | 
			
		||||
        start = torch.cuda.Event(enable_timing=True)
 | 
			
		||||
        end = torch.cuda.Event(enable_timing=True)
 | 
			
		||||
        start.record()
 | 
			
		||||
        torch.cuda._sleep(1000000)
 | 
			
		||||
        end.record()
 | 
			
		||||
        end.synchronize()
 | 
			
		||||
        _cycles_per_ms = 1000000 / start.elapsed_time(end)
 | 
			
		||||
    return _cycles_per_ms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def compare_cpu_gpu(tensor_constructor, arg_constructor, fn, t, precision=1e-5):
 | 
			
		||||
    def tmp(self):
 | 
			
		||||
        cpu_tensor = tensor_constructor(t)
 | 
			
		||||
        gpu_tensor = to_gpu(cpu_tensor)
 | 
			
		||||
        cpu_args = arg_constructor(t)
 | 
			
		||||
        gpu_args = [to_gpu(arg) for arg in cpu_args]
 | 
			
		||||
        cpu_result = getattr(cpu_tensor, fn)(*cpu_args)
 | 
			
		||||
        try:
 | 
			
		||||
            gpu_result = getattr(gpu_tensor, fn)(*gpu_args)
 | 
			
		||||
        except RuntimeError as e:
 | 
			
		||||
            reason = e.args[0]
 | 
			
		||||
            if 'unimplemented data type' in reason:
 | 
			
		||||
                raise unittest.SkipTest('unimplemented data type')
 | 
			
		||||
            raise
 | 
			
		||||
        except AttributeError as e:
 | 
			
		||||
            reason = e.args[0]
 | 
			
		||||
            if 'object has no attribute' in reason:
 | 
			
		||||
                raise unittest.SkipTest('unimplemented data type')
 | 
			
		||||
            raise
 | 
			
		||||
        # If one changes, another should change as well
 | 
			
		||||
        self.assertEqual(cpu_tensor, gpu_tensor, precision)
 | 
			
		||||
        self.assertEqual(cpu_args, gpu_args, precision)
 | 
			
		||||
        # Compare results
 | 
			
		||||
        self.assertEqual(cpu_result, gpu_result, precision)
 | 
			
		||||
    return tmp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCuda(TestCase):
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_autogpu(self):
 | 
			
		||||
        x = torch.randn(5, 5).cuda()
 | 
			
		||||
        y = torch.randn(5, 5).cuda()
 | 
			
		||||
        self.assertEqual(x.get_device(), 0)
 | 
			
		||||
        self.assertEqual(x.get_device(), 0)
 | 
			
		||||
        with torch.cuda.device(1):
 | 
			
		||||
            z = torch.randn(5, 5).cuda()
 | 
			
		||||
            self.assertEqual(z.get_device(), 1)
 | 
			
		||||
            q = x.add(y)
 | 
			
		||||
            self.assertEqual(q.get_device(), 0)
 | 
			
		||||
            w = torch.randn(5, 5).cuda()
 | 
			
		||||
            self.assertEqual(w.get_device(), 1)
 | 
			
		||||
        z = z.cuda()
 | 
			
		||||
        self.assertEqual(z.get_device(), 0)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_copy_device(self):
 | 
			
		||||
        x = torch.randn(5, 5).cuda()
 | 
			
		||||
        with torch.cuda.device(1):
 | 
			
		||||
            y = x.cuda()
 | 
			
		||||
            self.assertEqual(y.get_device(), 1)
 | 
			
		||||
            self.assertIs(y.cuda(), y)
 | 
			
		||||
            z = y.cuda(0)
 | 
			
		||||
            self.assertEqual(z.get_device(), 0)
 | 
			
		||||
            self.assertIs(z.cuda(0), z)
 | 
			
		||||
 | 
			
		||||
        x = torch.randn(5, 5)
 | 
			
		||||
        with torch.cuda.device(1):
 | 
			
		||||
            y = x.cuda()
 | 
			
		||||
            self.assertEqual(y.get_device(), 1)
 | 
			
		||||
            self.assertIs(y.cuda(), y)
 | 
			
		||||
            z = y.cuda(0)
 | 
			
		||||
            self.assertEqual(z.get_device(), 0)
 | 
			
		||||
            self.assertIs(z.cuda(0), z)
 | 
			
		||||
 | 
			
		||||
    def test_serialization_array_with_storage(self):
 | 
			
		||||
        x = torch.randn(5, 5).cuda()
 | 
			
		||||
        y = torch.IntTensor(2, 5).fill_(0).cuda()
 | 
			
		||||
        q = [x, y, x, y.storage()]
 | 
			
		||||
        with tempfile.NamedTemporaryFile() as f:
 | 
			
		||||
            torch.save(q, f)
 | 
			
		||||
            f.seek(0)
 | 
			
		||||
            q_copy = torch.load(f)
 | 
			
		||||
        self.assertEqual(q_copy, q, 0)
 | 
			
		||||
        q_copy[0].fill_(5)
 | 
			
		||||
        self.assertEqual(q_copy[0], q_copy[2], 0)
 | 
			
		||||
        self.assertTrue(isinstance(q_copy[0], torch.cuda.DoubleTensor))
 | 
			
		||||
        self.assertTrue(isinstance(q_copy[1], torch.cuda.IntTensor))
 | 
			
		||||
        self.assertTrue(isinstance(q_copy[2], torch.cuda.DoubleTensor))
 | 
			
		||||
        self.assertTrue(isinstance(q_copy[3], torch.cuda.IntStorage))
 | 
			
		||||
        q_copy[1].fill_(10)
 | 
			
		||||
        self.assertTrue(q_copy[3], torch.cuda.IntStorage(10).fill_(10))
 | 
			
		||||
 | 
			
		||||
    def test_type_conversions(self):
 | 
			
		||||
        x = torch.randn(5, 5)
 | 
			
		||||
        self.assertIs(type(x.float()), torch.FloatTensor)
 | 
			
		||||
        self.assertIs(type(x.cuda()), torch.cuda.DoubleTensor)
 | 
			
		||||
        self.assertIs(type(x.cuda().float()), torch.cuda.FloatTensor)
 | 
			
		||||
        self.assertIs(type(x.cuda().float().cpu()), torch.FloatTensor)
 | 
			
		||||
        self.assertIs(type(x.cuda().float().cpu().int()), torch.IntTensor)
 | 
			
		||||
 | 
			
		||||
        y = x.storage()
 | 
			
		||||
        self.assertIs(type(y.float()), torch.FloatStorage)
 | 
			
		||||
        self.assertIs(type(y.cuda()), torch.cuda.DoubleStorage)
 | 
			
		||||
        self.assertIs(type(y.cuda().float()), torch.cuda.FloatStorage)
 | 
			
		||||
        self.assertIs(type(y.cuda().float().cpu()), torch.FloatStorage)
 | 
			
		||||
        self.assertIs(type(y.cuda().float().cpu().int()), torch.IntStorage)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_type_conversions_same_gpu(self):
 | 
			
		||||
        x = torch.randn(5, 5).cuda(1)
 | 
			
		||||
        self.assertEqual(x.int().get_device(), 1)
 | 
			
		||||
 | 
			
		||||
    def _test_broadcast(self, input):
 | 
			
		||||
        if torch.cuda.device_count() < 2:
 | 
			
		||||
            raise unittest.SkipTest("only one GPU detected")
 | 
			
		||||
        result = comm.broadcast(input, (0, 1))
 | 
			
		||||
        for i, t in enumerate(result):
 | 
			
		||||
            self.assertEqual(t.get_device(), i)
 | 
			
		||||
            self.assertEqual(t, input)
 | 
			
		||||
 | 
			
		||||
    def test_broadcast_cpu(self):
 | 
			
		||||
        self._test_broadcast(torch.randn(5, 5))
 | 
			
		||||
 | 
			
		||||
    def test_broadcast_gpu(self):
 | 
			
		||||
        self._test_broadcast(torch.randn(5, 5))
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_broadcast_coalesced(self):
 | 
			
		||||
        numel = 5
 | 
			
		||||
        num_bytes = numel * 8
 | 
			
		||||
        tensors = [
 | 
			
		||||
            torch.randn(numel).long().cuda(),
 | 
			
		||||
            torch.randn(numel).cuda(),
 | 
			
		||||
            torch.randn(numel).long().cuda(),
 | 
			
		||||
            torch.randn(numel).long().cuda(),
 | 
			
		||||
            torch.randn(numel * 2).int().cuda(),  # int is 2x shorter
 | 
			
		||||
            torch.randn(numel).cuda(),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        b_tensors = [comm.broadcast(t, (0, 1)) for t in tensors]
 | 
			
		||||
        for (_, bt), t in zip(b_tensors, tensors):
 | 
			
		||||
            self.assertEqual(bt.get_device(), 1)
 | 
			
		||||
            self.assertEqual(bt, t)
 | 
			
		||||
            self.assertIsInstance(bt, type(t))
 | 
			
		||||
 | 
			
		||||
        bc_tensors = comm.broadcast_coalesced(tensors, (0, 1), buffer_size=num_bytes * 5 // 2)
 | 
			
		||||
        bc_tensors_t = list(zip(*bc_tensors))
 | 
			
		||||
        self.assertEqual(b_tensors, bc_tensors_t)
 | 
			
		||||
        for (_, bt), (_, bct) in zip(b_tensors, bc_tensors_t):
 | 
			
		||||
            self.assertEqual(bt.get_device(), bct.get_device())
 | 
			
		||||
            self.assertIsInstance(bct, type(bt))
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_reduce_add(self):
 | 
			
		||||
        x = torch.randn(5, 5)
 | 
			
		||||
        y = torch.randn(5, 5)
 | 
			
		||||
        x_cuda = x.cuda(0)
 | 
			
		||||
        y_cuda = y.cuda(1)
 | 
			
		||||
        result = comm.reduce_add((x_cuda, y_cuda))
 | 
			
		||||
        self.assertEqual(result.get_device(), 0)
 | 
			
		||||
        self.assertEqual(result.cpu(), x + y)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_reduce_add_coalesced(self):
 | 
			
		||||
        numel = 5
 | 
			
		||||
        num_bytes = numel * 8
 | 
			
		||||
        tensors = [
 | 
			
		||||
            torch.randn(numel).long().cuda(),
 | 
			
		||||
            torch.randn(numel).cuda(),
 | 
			
		||||
            torch.randn(numel).long().cuda(),
 | 
			
		||||
            torch.randn(numel).long().cuda(),
 | 
			
		||||
            torch.randn(numel * 2).int().cuda(),  # int is 2x shorter
 | 
			
		||||
            torch.randn(numel).cuda(),
 | 
			
		||||
        ]
 | 
			
		||||
        dup_tensors = [tensors, list(map(lambda t: t.cuda(1), tensors))]
 | 
			
		||||
 | 
			
		||||
        r_tensors = list(map(comm.reduce_add, zip(*dup_tensors)))
 | 
			
		||||
        for r, t in zip(r_tensors, tensors):
 | 
			
		||||
            self.assertEqual(r.get_device(), t.get_device())
 | 
			
		||||
            self.assertEqual(r, t * 2)
 | 
			
		||||
            self.assertIsInstance(r, type(t))
 | 
			
		||||
 | 
			
		||||
        rc_tensors = comm.reduce_add_coalesced(dup_tensors, buffer_size=num_bytes * 5 // 2)
 | 
			
		||||
        self.assertEqual(r_tensors, rc_tensors)
 | 
			
		||||
        for r, rc in zip(r_tensors, rc_tensors):
 | 
			
		||||
            self.assertEqual(rc.get_device(), r.get_device())
 | 
			
		||||
            self.assertIsInstance(rc, type(r))
 | 
			
		||||
 | 
			
		||||
    def _test_scatter(self, input, chunk_sizes=None, dim=0):
 | 
			
		||||
        if torch.cuda.device_count() < 2:
 | 
			
		||||
            raise unittest.SkipTest("only one GPU detected")
 | 
			
		||||
        result = comm.scatter(input, (0, 1), chunk_sizes, dim)
 | 
			
		||||
        self.assertEqual(len(result), 2)
 | 
			
		||||
        if chunk_sizes is None:
 | 
			
		||||
            chunk_sizes = tuple(repeat(input.size(dim) // 2, 2))
 | 
			
		||||
        chunk_start = 0
 | 
			
		||||
        for i, r in enumerate(result):
 | 
			
		||||
            chunk_end = chunk_start + chunk_sizes[i]
 | 
			
		||||
            index = [slice(None, None), slice(None, None)]
 | 
			
		||||
            index[dim] = slice(chunk_start, chunk_end)
 | 
			
		||||
            self.assertEqual(r, input[tuple(index)], 0)
 | 
			
		||||
            chunk_start = chunk_end
 | 
			
		||||
 | 
			
		||||
    def test_scatter_cpu(self):
 | 
			
		||||
        self._test_scatter(torch.randn(4, 4), dim=0)
 | 
			
		||||
 | 
			
		||||
    def test_scatter_cpu_dim(self):
 | 
			
		||||
        self._test_scatter(torch.randn(4, 4), dim=1)
 | 
			
		||||
 | 
			
		||||
    def test_scatter_cpu_neg_dim(self):
 | 
			
		||||
        self._test_scatter(torch.randn(4, 4), dim=-2)
 | 
			
		||||
 | 
			
		||||
    def test_scatter_cpu_sizes(self):
 | 
			
		||||
        self._test_scatter(torch.randn(6, 4), chunk_sizes=(2, 4))
 | 
			
		||||
 | 
			
		||||
    def test_scatter_gpu(self):
 | 
			
		||||
        self._test_scatter(torch.randn(4, 4).cuda(), dim=0)
 | 
			
		||||
 | 
			
		||||
    def test_scatter_gpu_dim(self):
 | 
			
		||||
        self._test_scatter(torch.randn(4, 4).cuda(), dim=1)
 | 
			
		||||
 | 
			
		||||
    def test_scatter_gpu_neg_dim(self):
 | 
			
		||||
        self._test_scatter(torch.randn(4, 4).cuda(), dim=-2)
 | 
			
		||||
 | 
			
		||||
    def test_scatter_gpu_sizes(self):
 | 
			
		||||
        self._test_scatter(torch.randn(6, 4).cuda(), chunk_sizes=(2, 4))
 | 
			
		||||
 | 
			
		||||
    def _test_gather(self, dim):
 | 
			
		||||
        if torch.cuda.device_count() < 2:
 | 
			
		||||
            raise unittest.SkipTest("only one GPU detected")
 | 
			
		||||
        x = torch.randn(2, 5).cuda(0)
 | 
			
		||||
        y = torch.randn(2, 5).cuda(1)
 | 
			
		||||
        result = comm.gather((x, y), dim)
 | 
			
		||||
 | 
			
		||||
        expected_size = list(x.size())
 | 
			
		||||
        expected_size[dim] += y.size(dim)
 | 
			
		||||
        expected_size = torch.Size(expected_size)
 | 
			
		||||
        self.assertEqual(result.get_device(), 0)
 | 
			
		||||
        self.assertEqual(result.size(), expected_size)
 | 
			
		||||
 | 
			
		||||
        index = [slice(None, None), slice(None, None)]
 | 
			
		||||
        index[dim] = slice(0, x.size(dim))
 | 
			
		||||
        self.assertEqual(result[tuple(index)], x)
 | 
			
		||||
        index[dim] = slice(x.size(dim), x.size(dim) + y.size(dim))
 | 
			
		||||
        self.assertEqual(result[tuple(index)], y)
 | 
			
		||||
 | 
			
		||||
    def test_gather(self):
 | 
			
		||||
        self._test_gather(0)
 | 
			
		||||
 | 
			
		||||
    def test_gather_dim(self):
 | 
			
		||||
        self._test_gather(1)
 | 
			
		||||
 | 
			
		||||
    def test_from_sequence(self):
 | 
			
		||||
        seq = [list(range(i * 4, i * 4 + 4)) for i in range(5)]
 | 
			
		||||
        reference = torch.arange(0, 20).resize_(5, 4)
 | 
			
		||||
        for t in types:
 | 
			
		||||
            cuda_type = get_gpu_type(t)
 | 
			
		||||
            self.assertEqual(cuda_type(seq), reference)
 | 
			
		||||
 | 
			
		||||
    def test_torch_manual_seed_seeds_cuda_devices(self):
 | 
			
		||||
        with freeze_rng_state():
 | 
			
		||||
            x = torch.zeros(4, 4).float().cuda()
 | 
			
		||||
            torch.manual_seed(2)
 | 
			
		||||
            self.assertEqual(torch.cuda.initial_seed(), 2)
 | 
			
		||||
            x.uniform_()
 | 
			
		||||
            torch.manual_seed(2)
 | 
			
		||||
            y = x.clone().uniform_()
 | 
			
		||||
            self.assertEqual(x, y)
 | 
			
		||||
            self.assertEqual(torch.cuda.initial_seed(), 2)
 | 
			
		||||
 | 
			
		||||
    def test_manual_seed(self):
 | 
			
		||||
        with freeze_rng_state():
 | 
			
		||||
            x = torch.zeros(4, 4).float().cuda()
 | 
			
		||||
            torch.cuda.manual_seed(2)
 | 
			
		||||
            self.assertEqual(torch.cuda.initial_seed(), 2)
 | 
			
		||||
            x.uniform_()
 | 
			
		||||
            torch.cuda.manual_seed(2)
 | 
			
		||||
            y = x.clone().uniform_()
 | 
			
		||||
            self.assertEqual(x, y)
 | 
			
		||||
            self.assertEqual(torch.cuda.initial_seed(), 2)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_cat_autogpu(self):
 | 
			
		||||
        x = torch.randn(4, 4).cuda(1)
 | 
			
		||||
        y = torch.randn(4, 4).cuda(1)
 | 
			
		||||
        z = torch.cat([x, y], 0)
 | 
			
		||||
        self.assertEqual(z.get_device(), x.get_device())
 | 
			
		||||
 | 
			
		||||
    def test_serialization(self):
 | 
			
		||||
        x = torch.randn(4, 4).cuda()
 | 
			
		||||
        with tempfile.NamedTemporaryFile() as f:
 | 
			
		||||
            torch.save(x, f)
 | 
			
		||||
            f.seek(0)
 | 
			
		||||
            x_copy = torch.load(f)
 | 
			
		||||
        self.assertEqual(x_copy, x)
 | 
			
		||||
        self.assertIs(type(x_copy), type(x))
 | 
			
		||||
        self.assertEqual(x_copy.get_device(), x.get_device())
 | 
			
		||||
 | 
			
		||||
    def test_serialization_array_with_empty(self):
 | 
			
		||||
        x = [torch.randn(4, 4).cuda(), torch.cuda.FloatTensor()]
 | 
			
		||||
        with tempfile.NamedTemporaryFile() as f:
 | 
			
		||||
            torch.save(x, f)
 | 
			
		||||
            f.seek(0)
 | 
			
		||||
            x_copy = torch.load(f)
 | 
			
		||||
        for original, copy in zip(x, x_copy):
 | 
			
		||||
            self.assertEqual(copy, original)
 | 
			
		||||
            self.assertIs(type(copy), type(original))
 | 
			
		||||
            self.assertEqual(copy.get_device(), original.get_device())
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "detected only one GPU")
 | 
			
		||||
    def test_multigpu_serialization(self):
 | 
			
		||||
        x = [torch.randn(4, 4).cuda(0), torch.randn(4, 4).cuda(1)]
 | 
			
		||||
        with tempfile.NamedTemporaryFile() as f:
 | 
			
		||||
            torch.save(x, f)
 | 
			
		||||
            f.seek(0)
 | 
			
		||||
            x_copy = torch.load(f)
 | 
			
		||||
        for original, copy in zip(x, x_copy):
 | 
			
		||||
            self.assertEqual(copy, original)
 | 
			
		||||
            self.assertIs(type(copy), type(original))
 | 
			
		||||
            self.assertEqual(copy.get_device(), original.get_device())
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "detected only one GPU")
 | 
			
		||||
    def test_multigpu_serialization_remap(self):
 | 
			
		||||
        x = [torch.randn(4, 4).cuda(0), torch.randn(4, 4).cuda(1)]
 | 
			
		||||
 | 
			
		||||
        def gpu_remap(storage, location):
 | 
			
		||||
            if location == 'cuda:1':
 | 
			
		||||
                return storage.cuda(0)
 | 
			
		||||
 | 
			
		||||
        with tempfile.NamedTemporaryFile() as f:
 | 
			
		||||
            torch.save(x, f)
 | 
			
		||||
            f.seek(0)
 | 
			
		||||
            x_copy = torch.load(f, map_location=gpu_remap)
 | 
			
		||||
 | 
			
		||||
        for original, copy in zip(x, x_copy):
 | 
			
		||||
            self.assertEqual(copy, original)
 | 
			
		||||
            self.assertIs(type(copy), type(original))
 | 
			
		||||
            self.assertEqual(copy.get_device(), 0)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "detected only one GPU")
 | 
			
		||||
    def test_multigpu_serialization_remap_dict(self):
 | 
			
		||||
        x = [torch.randn(4, 4).cuda(0), torch.randn(4, 4).cuda(1)]
 | 
			
		||||
        with tempfile.NamedTemporaryFile() as f:
 | 
			
		||||
            torch.save(x, f)
 | 
			
		||||
            f.seek(0)
 | 
			
		||||
            x_copy = torch.load(f, map_location={'cuda:1': 'cuda:0'})
 | 
			
		||||
        for original, copy in zip(x, x_copy):
 | 
			
		||||
            self.assertEqual(copy, original)
 | 
			
		||||
            self.assertIs(type(copy), type(original))
 | 
			
		||||
            self.assertEqual(copy.get_device(), 0)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "detected only one GPU")
 | 
			
		||||
    def test_cuda_set_device(self):
 | 
			
		||||
        x = torch.randn(5, 5)
 | 
			
		||||
        with torch.cuda.device(1):
 | 
			
		||||
            self.assertEqual(x.cuda().get_device(), 1)
 | 
			
		||||
            torch.cuda.set_device(0)
 | 
			
		||||
            self.assertEqual(x.cuda().get_device(), 0)
 | 
			
		||||
            with torch.cuda.device(1):
 | 
			
		||||
                self.assertEqual(x.cuda().get_device(), 1)
 | 
			
		||||
            self.assertEqual(x.cuda().get_device(), 0)
 | 
			
		||||
            torch.cuda.set_device(1)
 | 
			
		||||
        self.assertEqual(x.cuda().get_device(), 0)
 | 
			
		||||
 | 
			
		||||
    def test_is_tensor(self):
 | 
			
		||||
        for t in types:
 | 
			
		||||
            tensor = get_gpu_type(t)()
 | 
			
		||||
            self.assertTrue(torch.is_tensor(tensor))
 | 
			
		||||
        self.assertTrue(torch.is_tensor(torch.cuda.HalfTensor()))
 | 
			
		||||
 | 
			
		||||
    def test_cuda_synchronize(self):
 | 
			
		||||
        torch.cuda.synchronize()
 | 
			
		||||
 | 
			
		||||
    def test_streams(self):
 | 
			
		||||
        default_stream = torch.cuda.current_stream()
 | 
			
		||||
        user_stream = torch.cuda.Stream()
 | 
			
		||||
        self.assertEqual(torch.cuda.current_stream(), default_stream)
 | 
			
		||||
        self.assertNotEqual(default_stream, user_stream)
 | 
			
		||||
        self.assertEqual(default_stream.cuda_stream, 0)
 | 
			
		||||
        self.assertNotEqual(user_stream.cuda_stream, 0)
 | 
			
		||||
        with torch.cuda.stream(user_stream):
 | 
			
		||||
            self.assertEqual(torch.cuda.current_stream(), user_stream)
 | 
			
		||||
        self.assertTrue(user_stream.query())
 | 
			
		||||
        # copy 10 MB tensor from CPU-GPU which should take some time
 | 
			
		||||
        tensor1 = torch.ByteTensor(10000000).pin_memory()
 | 
			
		||||
        tensor2 = tensor1.cuda(async=True)
 | 
			
		||||
        self.assertFalse(default_stream.query())
 | 
			
		||||
        default_stream.synchronize()
 | 
			
		||||
        self.assertTrue(default_stream.query())
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "detected only one GPU")
 | 
			
		||||
    def test_streams_multi_gpu(self):
 | 
			
		||||
        default_stream = torch.cuda.current_stream()
 | 
			
		||||
        self.assertEqual(default_stream.device, 0)
 | 
			
		||||
        stream = torch.cuda.Stream(device=1)
 | 
			
		||||
        self.assertEqual(stream.device, 1)
 | 
			
		||||
        with torch.cuda.device(1):
 | 
			
		||||
            self.assertEqual(torch.cuda.current_stream().device, 1)
 | 
			
		||||
            self.assertNotEqual(torch.cuda.current_stream(), default_stream)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "multi-GPU not supported")
 | 
			
		||||
    def test_tensor_device(self):
 | 
			
		||||
        self.assertEqual(torch.cuda.FloatTensor(1).get_device(), 0)
 | 
			
		||||
        self.assertEqual(torch.cuda.FloatTensor(1, device=1).get_device(), 1)
 | 
			
		||||
        with torch.cuda.device(1):
 | 
			
		||||
            self.assertEqual(torch.cuda.FloatTensor(1).get_device(), 1)
 | 
			
		||||
            self.assertEqual(torch.cuda.FloatTensor(1, device=0).get_device(), 0)
 | 
			
		||||
            self.assertEqual(torch.cuda.FloatTensor(1, device=None).get_device(), 1)
 | 
			
		||||
 | 
			
		||||
    def test_events(self):
 | 
			
		||||
        stream = torch.cuda.current_stream()
 | 
			
		||||
        event = torch.cuda.Event(enable_timing=True)
 | 
			
		||||
        self.assertTrue(event.query())
 | 
			
		||||
        start_event = torch.cuda.Event(enable_timing=True)
 | 
			
		||||
        stream.record_event(start_event)
 | 
			
		||||
        torch.cuda._sleep(int(50 * get_cycles_per_ms()))
 | 
			
		||||
        stream.record_event(event)
 | 
			
		||||
        self.assertFalse(event.query())
 | 
			
		||||
        event.synchronize()
 | 
			
		||||
        self.assertTrue(event.query())
 | 
			
		||||
        self.assertGreater(start_event.elapsed_time(event), 0)
 | 
			
		||||
 | 
			
		||||
    def test_record_stream(self):
 | 
			
		||||
        cycles_per_ms = get_cycles_per_ms()
 | 
			
		||||
 | 
			
		||||
        t = torch.FloatTensor([1, 2, 3, 4]).pin_memory()
 | 
			
		||||
        result = torch.cuda.FloatTensor(t.size())
 | 
			
		||||
        stream = torch.cuda.Stream()
 | 
			
		||||
        ptr = [None]
 | 
			
		||||
 | 
			
		||||
        # Performs the CPU->GPU copy in a background stream
 | 
			
		||||
        def perform_copy():
 | 
			
		||||
            with torch.cuda.stream(stream):
 | 
			
		||||
                tmp = t.cuda(async=True)
 | 
			
		||||
                ptr[0] = tmp.data_ptr()
 | 
			
		||||
            torch.cuda.current_stream().wait_stream(stream)
 | 
			
		||||
            tmp.record_stream(torch.cuda.current_stream())
 | 
			
		||||
            torch.cuda._sleep(int(50 * cycles_per_ms))  # delay the copy
 | 
			
		||||
            result.copy_(tmp)
 | 
			
		||||
 | 
			
		||||
        perform_copy()
 | 
			
		||||
        with torch.cuda.stream(stream):
 | 
			
		||||
            tmp2 = torch.cuda.FloatTensor(t.size())
 | 
			
		||||
            tmp2.zero_()
 | 
			
		||||
            self.assertNotEqual(tmp2.data_ptr(), ptr[0], 'allocation re-used to soon')
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(result.tolist(), [1, 2, 3, 4])
 | 
			
		||||
 | 
			
		||||
        # Check that the block will be re-used after the main stream finishes
 | 
			
		||||
        torch.cuda.current_stream().synchronize()
 | 
			
		||||
        with torch.cuda.stream(stream):
 | 
			
		||||
            tmp3 = torch.cuda.FloatTensor(t.size())
 | 
			
		||||
            self.assertEqual(tmp3.data_ptr(), ptr[0], 'allocation not re-used')
 | 
			
		||||
 | 
			
		||||
    def test_caching_pinned_memory(self):
 | 
			
		||||
        cycles_per_ms = get_cycles_per_ms()
 | 
			
		||||
 | 
			
		||||
        # check that allocations are re-used after deletion
 | 
			
		||||
        t = torch.FloatTensor([1]).pin_memory()
 | 
			
		||||
        ptr = t.data_ptr()
 | 
			
		||||
        del t
 | 
			
		||||
        t = torch.FloatTensor([1]).pin_memory()
 | 
			
		||||
        self.assertEqual(t.data_ptr(), ptr, 'allocation not reused')
 | 
			
		||||
 | 
			
		||||
        # check that the allocation is not re-used if it's in-use by a copy
 | 
			
		||||
        gpu_tensor = torch.cuda.FloatTensor([0])
 | 
			
		||||
        torch.cuda._sleep(int(50 * cycles_per_ms))  # delay the copy
 | 
			
		||||
        gpu_tensor.copy_(t, async=True)
 | 
			
		||||
        del t
 | 
			
		||||
        t = torch.FloatTensor([1]).pin_memory()
 | 
			
		||||
        self.assertNotEqual(t.data_ptr(), ptr, 'allocation re-used too soon')
 | 
			
		||||
        self.assertEqual(list(gpu_tensor), [1])
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_caching_pinned_memory_multi_gpu(self):
 | 
			
		||||
        # checks that the events preventing pinned memory from being re-used
 | 
			
		||||
        # too early are recorded on the correct GPU
 | 
			
		||||
        cycles_per_ms = get_cycles_per_ms()
 | 
			
		||||
 | 
			
		||||
        t = torch.FloatTensor([1]).pin_memory()
 | 
			
		||||
        ptr = t.data_ptr()
 | 
			
		||||
        gpu_tensor0 = torch.cuda.FloatTensor([0], device=0)
 | 
			
		||||
        gpu_tensor1 = torch.cuda.FloatTensor([0], device=1)
 | 
			
		||||
 | 
			
		||||
        with torch.cuda.device(1):
 | 
			
		||||
            torch.cuda._sleep(int(50 * cycles_per_ms))  # delay the copy
 | 
			
		||||
            gpu_tensor1.copy_(t, async=True)
 | 
			
		||||
 | 
			
		||||
        del t
 | 
			
		||||
        t = torch.FloatTensor([2]).pin_memory()
 | 
			
		||||
        self.assertNotEqual(t.data_ptr(), ptr, 'allocation re-used too soon')
 | 
			
		||||
 | 
			
		||||
        with torch.cuda.device(0):
 | 
			
		||||
            gpu_tensor0.copy_(t, async=True)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(gpu_tensor1[0], 1)
 | 
			
		||||
        self.assertEqual(gpu_tensor0[0], 2)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _select_broadcastable_dims(dims_full=None):
 | 
			
		||||
        return TestTorch._select_broadcastable_dims(dims_full)
 | 
			
		||||
 | 
			
		||||
    def test_broadcast(self):
 | 
			
		||||
        TestTorch._test_broadcast(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_broadcast_fallback(self):
 | 
			
		||||
        TestTorch._test_broadcast_fallback(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_broadcast_fused_matmul(self):
 | 
			
		||||
        TestTorch._test_broadcast_fused_matmul(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_broadcast_batched_matmul(self):
 | 
			
		||||
        TestTorch._test_broadcast_batched_matmul(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_advancedindex(self):
 | 
			
		||||
        TestTorch._test_advancedindex(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_advancedindex_big(self):
 | 
			
		||||
        TestTorch._test_advancedindex_big(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_btrifact(self):
 | 
			
		||||
        TestTorch._test_btrifact(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_btrisolve(self):
 | 
			
		||||
        TestTorch._test_btrisolve(self, lambda t: t.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_tensor_gather(self):
 | 
			
		||||
        TestTorch._test_gather(self, lambda t: t.cuda(), False)
 | 
			
		||||
 | 
			
		||||
    def test_tensor_scatter(self):
 | 
			
		||||
        TestTorch._test_scatter_base(self, lambda t: t.cuda(), 'scatter_', test_bounds=False)
 | 
			
		||||
 | 
			
		||||
    def test_tensor_scatterAdd(self):
 | 
			
		||||
        TestTorch._test_scatter_base(self, lambda t: t.cuda(), 'scatter_add_', test_bounds=False)
 | 
			
		||||
 | 
			
		||||
    def test_tensor_scatterFill(self):
 | 
			
		||||
        TestTorch._test_scatter_base(self, lambda t: t.cuda(), 'scatter_', True, test_bounds=False)
 | 
			
		||||
 | 
			
		||||
    def test_arange(self):
 | 
			
		||||
        for t in ['IntTensor', 'LongTensor', 'FloatTensor', 'DoubleTensor']:
 | 
			
		||||
            a = torch.cuda.__dict__[t]()
 | 
			
		||||
            torch.arange(0, 10, out=a)
 | 
			
		||||
            b = torch.__dict__[t]()
 | 
			
		||||
            torch.arange(0, 10, out=b)
 | 
			
		||||
            self.assertEqual(a, b.cuda())
 | 
			
		||||
 | 
			
		||||
    def test_nvtx(self):
 | 
			
		||||
        # Just making sure we can see the symbols
 | 
			
		||||
        torch.cuda.nvtx.range_push("foo")
 | 
			
		||||
        torch.cuda.nvtx.mark("bar")
 | 
			
		||||
        torch.cuda.nvtx.range_pop()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if HAS_CUDA:
 | 
			
		||||
    for decl in tests:
 | 
			
		||||
        for t in types:
 | 
			
		||||
            tensor = t()
 | 
			
		||||
            gpu_tensor = get_gpu_type(t)()
 | 
			
		||||
            if len(decl) == 3:
 | 
			
		||||
                name, constr, arg_constr = decl
 | 
			
		||||
                desc = ''
 | 
			
		||||
            elif len(decl) == 4:
 | 
			
		||||
                name, constr, arg_constr, desc = decl
 | 
			
		||||
            elif len(decl) == 5:
 | 
			
		||||
                name, constr, arg_constr, desc, type_subset = decl
 | 
			
		||||
                if t not in type_subset:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
            precision = custom_precision.get(name, TestCuda.precision)
 | 
			
		||||
            for inplace in (True, False):
 | 
			
		||||
                if inplace:
 | 
			
		||||
                    name_inner = name + '_'
 | 
			
		||||
                else:
 | 
			
		||||
                    name_inner = name
 | 
			
		||||
                if not hasattr(tensor, name_inner):
 | 
			
		||||
                    continue
 | 
			
		||||
                if not hasattr(gpu_tensor, name_inner):
 | 
			
		||||
                    print("Ignoring {}, because it's not implemented by torch.cuda.{}".format(
 | 
			
		||||
                        name_inner, gpu_tensor.__class__.__name__))
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                test_name = 'test_' + t.__name__ + '_' + name_inner
 | 
			
		||||
                if desc:
 | 
			
		||||
                    test_name += '_' + desc
 | 
			
		||||
 | 
			
		||||
                assert not hasattr(TestCuda, test_name), "Duplicated test name: " + test_name
 | 
			
		||||
                setattr(TestCuda, test_name, compare_cpu_gpu(constr, arg_constr, name_inner, t, precision))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    run_tests()
 | 
			
		||||
							
								
								
									
										337
									
								
								test/test_dataloader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								test/test_dataloader.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,337 @@
 | 
			
		||||
import math
 | 
			
		||||
import sys
 | 
			
		||||
import torch
 | 
			
		||||
import traceback
 | 
			
		||||
import unittest
 | 
			
		||||
from torch.utils.data import Dataset, TensorDataset, DataLoader, ConcatDataset
 | 
			
		||||
from common import TestCase, run_tests, TEST_NUMPY
 | 
			
		||||
from common_nn import TEST_CUDA
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestTensorDataset(TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_len(self):
 | 
			
		||||
        source = TensorDataset(torch.randn(15, 10, 2, 3, 4, 5), torch.randperm(15))
 | 
			
		||||
        self.assertEqual(len(source), 15)
 | 
			
		||||
 | 
			
		||||
    def test_getitem(self):
 | 
			
		||||
        t = torch.randn(15, 10, 2, 3, 4, 5)
 | 
			
		||||
        l = torch.randn(15, 10)
 | 
			
		||||
        source = TensorDataset(t, l)
 | 
			
		||||
        for i in range(15):
 | 
			
		||||
            self.assertEqual(t[i], source[i][0])
 | 
			
		||||
            self.assertEqual(l[i], source[i][1])
 | 
			
		||||
 | 
			
		||||
    def test_getitem_1d(self):
 | 
			
		||||
        t = torch.randn(15)
 | 
			
		||||
        l = torch.randn(15)
 | 
			
		||||
        source = TensorDataset(t, l)
 | 
			
		||||
        for i in range(15):
 | 
			
		||||
            self.assertEqual(t[i], source[i][0])
 | 
			
		||||
            self.assertEqual(l[i], source[i][1])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestConcatDataset(TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_concat_two_singletons(self):
 | 
			
		||||
        result = ConcatDataset([[0], [1]])
 | 
			
		||||
        self.assertEqual(2, len(result))
 | 
			
		||||
        self.assertEqual(0, result[0])
 | 
			
		||||
        self.assertEqual(1, result[1])
 | 
			
		||||
 | 
			
		||||
    def test_concat_two_non_singletons(self):
 | 
			
		||||
        result = ConcatDataset([[0, 1, 2, 3, 4],
 | 
			
		||||
                                [5, 6, 7, 8, 9]])
 | 
			
		||||
        self.assertEqual(10, len(result))
 | 
			
		||||
        self.assertEqual(0, result[0])
 | 
			
		||||
        self.assertEqual(5, result[5])
 | 
			
		||||
 | 
			
		||||
    def test_concat_two_non_singletons_with_empty(self):
 | 
			
		||||
        # Adding an empty dataset somewhere is correctly handled
 | 
			
		||||
        result = ConcatDataset([[0, 1, 2, 3, 4],
 | 
			
		||||
                                [],
 | 
			
		||||
                                [5, 6, 7, 8, 9]])
 | 
			
		||||
        self.assertEqual(10, len(result))
 | 
			
		||||
        self.assertEqual(0, result[0])
 | 
			
		||||
        self.assertEqual(5, result[5])
 | 
			
		||||
 | 
			
		||||
    def test_concat_raises_index_error(self):
 | 
			
		||||
        result = ConcatDataset([[0, 1, 2, 3, 4],
 | 
			
		||||
                                [5, 6, 7, 8, 9]])
 | 
			
		||||
        with self.assertRaises(IndexError):
 | 
			
		||||
            # this one goes to 11
 | 
			
		||||
            result[11]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ErrorDataset(Dataset):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size):
 | 
			
		||||
        self.size = size
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return self.size
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDataLoader(TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.data = torch.randn(100, 2, 3, 5)
 | 
			
		||||
        self.labels = torch.randperm(50).repeat(2)
 | 
			
		||||
        self.dataset = TensorDataset(self.data, self.labels)
 | 
			
		||||
 | 
			
		||||
    def _test_sequential(self, loader):
 | 
			
		||||
        batch_size = loader.batch_size
 | 
			
		||||
        for i, (sample, target) in enumerate(loader):
 | 
			
		||||
            idx = i * batch_size
 | 
			
		||||
            self.assertEqual(sample, self.data[idx:idx + batch_size])
 | 
			
		||||
            self.assertEqual(target, self.labels[idx:idx + batch_size])
 | 
			
		||||
        self.assertEqual(i, math.floor((len(self.dataset) - 1) / batch_size))
 | 
			
		||||
 | 
			
		||||
    def _test_shuffle(self, loader):
 | 
			
		||||
        found_data = {i: 0 for i in range(self.data.size(0))}
 | 
			
		||||
        found_labels = {i: 0 for i in range(self.labels.size(0))}
 | 
			
		||||
        batch_size = loader.batch_size
 | 
			
		||||
        for i, (batch_samples, batch_targets) in enumerate(loader):
 | 
			
		||||
            for sample, target in zip(batch_samples, batch_targets):
 | 
			
		||||
                for data_point_idx, data_point in enumerate(self.data):
 | 
			
		||||
                    if data_point.eq(sample).all():
 | 
			
		||||
                        self.assertFalse(found_data[data_point_idx])
 | 
			
		||||
                        found_data[data_point_idx] += 1
 | 
			
		||||
                        break
 | 
			
		||||
                self.assertEqual(target, self.labels[data_point_idx])
 | 
			
		||||
                found_labels[data_point_idx] += 1
 | 
			
		||||
            self.assertEqual(sum(found_data.values()), (i + 1) * batch_size)
 | 
			
		||||
            self.assertEqual(sum(found_labels.values()), (i + 1) * batch_size)
 | 
			
		||||
        self.assertEqual(i, math.floor((len(self.dataset) - 1) / batch_size))
 | 
			
		||||
 | 
			
		||||
    def _test_error(self, loader):
 | 
			
		||||
        it = iter(loader)
 | 
			
		||||
        errors = 0
 | 
			
		||||
        while True:
 | 
			
		||||
            try:
 | 
			
		||||
                next(it)
 | 
			
		||||
            except NotImplementedError:
 | 
			
		||||
                errors += 1
 | 
			
		||||
            except StopIteration:
 | 
			
		||||
                self.assertEqual(errors,
 | 
			
		||||
                                 math.ceil(float(len(loader.dataset)) / loader.batch_size))
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
    def test_sequential(self):
 | 
			
		||||
        self._test_sequential(DataLoader(self.dataset))
 | 
			
		||||
 | 
			
		||||
    def test_sequential_batch(self):
 | 
			
		||||
        self._test_sequential(DataLoader(self.dataset, batch_size=2))
 | 
			
		||||
 | 
			
		||||
    def test_growing_dataset(self):
 | 
			
		||||
        dataset = [torch.ones(4) for _ in range(4)]
 | 
			
		||||
        dataloader_seq = DataLoader(dataset, shuffle=False)
 | 
			
		||||
        dataloader_shuffle = DataLoader(dataset, shuffle=True)
 | 
			
		||||
        dataset.append(torch.ones(4))
 | 
			
		||||
        self.assertEqual(len(dataloader_seq), 5)
 | 
			
		||||
        self.assertEqual(len(dataloader_shuffle), 5)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA, "CUDA unavailable")
 | 
			
		||||
    def test_sequential_pin_memory(self):
 | 
			
		||||
        loader = DataLoader(self.dataset, batch_size=2, pin_memory=True)
 | 
			
		||||
        for input, target in loader:
 | 
			
		||||
            self.assertTrue(input.is_pinned())
 | 
			
		||||
            self.assertTrue(target.is_pinned())
 | 
			
		||||
 | 
			
		||||
    def test_shuffle(self):
 | 
			
		||||
        self._test_shuffle(DataLoader(self.dataset, shuffle=True))
 | 
			
		||||
 | 
			
		||||
    def test_shuffle_batch(self):
 | 
			
		||||
        self._test_shuffle(DataLoader(self.dataset, batch_size=2, shuffle=True))
 | 
			
		||||
 | 
			
		||||
    def test_sequential_workers(self):
 | 
			
		||||
        self._test_sequential(DataLoader(self.dataset, num_workers=4))
 | 
			
		||||
 | 
			
		||||
    def test_seqential_batch_workers(self):
 | 
			
		||||
        self._test_sequential(DataLoader(self.dataset, batch_size=2, num_workers=4))
 | 
			
		||||
 | 
			
		||||
    def test_shuffle_workers(self):
 | 
			
		||||
        self._test_shuffle(DataLoader(self.dataset, shuffle=True, num_workers=4))
 | 
			
		||||
 | 
			
		||||
    def test_shuffle_batch_workers(self):
 | 
			
		||||
        self._test_shuffle(DataLoader(self.dataset, batch_size=2, shuffle=True, num_workers=4))
 | 
			
		||||
 | 
			
		||||
    def _test_batch_sampler(self, **kwargs):
 | 
			
		||||
        # [(0, 1), (2, 3, 4), (5, 6), (7, 8, 9), ...]
 | 
			
		||||
        batches = []
 | 
			
		||||
        for i in range(0, 100, 5):
 | 
			
		||||
            batches.append(tuple(range(i, i + 2)))
 | 
			
		||||
            batches.append(tuple(range(i + 2, i + 5)))
 | 
			
		||||
 | 
			
		||||
        dl = DataLoader(self.dataset, batch_sampler=batches, **kwargs)
 | 
			
		||||
        self.assertEqual(len(dl), 40)
 | 
			
		||||
        for i, (input, _target) in enumerate(dl):
 | 
			
		||||
            if i % 2 == 0:
 | 
			
		||||
                offset = i * 5 // 2
 | 
			
		||||
                self.assertEqual(len(input), 2)
 | 
			
		||||
                self.assertEqual(input, self.data[offset:offset + 2])
 | 
			
		||||
            else:
 | 
			
		||||
                offset = i * 5 // 2
 | 
			
		||||
                self.assertEqual(len(input), 3)
 | 
			
		||||
                self.assertEqual(input, self.data[offset:offset + 3])
 | 
			
		||||
 | 
			
		||||
    def test_batch_sampler(self):
 | 
			
		||||
        self._test_batch_sampler()
 | 
			
		||||
        self._test_batch_sampler(num_workers=4)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA, "CUDA unavailable")
 | 
			
		||||
    def test_shuffle_pin_memory(self):
 | 
			
		||||
        loader = DataLoader(self.dataset, batch_size=2, shuffle=True, num_workers=4, pin_memory=True)
 | 
			
		||||
        for input, target in loader:
 | 
			
		||||
            self.assertTrue(input.is_pinned())
 | 
			
		||||
            self.assertTrue(target.is_pinned())
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_NUMPY, "numpy unavailable")
 | 
			
		||||
    def test_numpy(self):
 | 
			
		||||
        import numpy as np
 | 
			
		||||
 | 
			
		||||
        class TestDataset(torch.utils.data.Dataset):
 | 
			
		||||
            def __getitem__(self, i):
 | 
			
		||||
                return np.ones((2, 3, 4)) * i
 | 
			
		||||
 | 
			
		||||
            def __len__(self):
 | 
			
		||||
                return 1000
 | 
			
		||||
 | 
			
		||||
        loader = DataLoader(TestDataset(), batch_size=12)
 | 
			
		||||
        batch = next(iter(loader))
 | 
			
		||||
        self.assertIsInstance(batch, torch.DoubleTensor)
 | 
			
		||||
        self.assertEqual(batch.size(), torch.Size([12, 2, 3, 4]))
 | 
			
		||||
 | 
			
		||||
    def test_error(self):
 | 
			
		||||
        self._test_error(DataLoader(ErrorDataset(100), batch_size=2, shuffle=True))
 | 
			
		||||
 | 
			
		||||
    def test_error_workers(self):
 | 
			
		||||
        self._test_error(DataLoader(ErrorDataset(41), batch_size=2, shuffle=True, num_workers=4))
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA, "CUDA unavailable")
 | 
			
		||||
    def test_partial_workers(self):
 | 
			
		||||
        "check that workers exit even if the iterator is not exhausted"
 | 
			
		||||
        loader = iter(DataLoader(self.dataset, batch_size=2, num_workers=4, pin_memory=True))
 | 
			
		||||
        workers = loader.workers
 | 
			
		||||
        pin_thread = loader.pin_thread
 | 
			
		||||
        for i, sample in enumerate(loader):
 | 
			
		||||
            if i == 3:
 | 
			
		||||
                break
 | 
			
		||||
        del loader
 | 
			
		||||
        for w in workers:
 | 
			
		||||
            w.join(1.0)  # timeout of one second
 | 
			
		||||
            self.assertFalse(w.is_alive(), 'subprocess not terminated')
 | 
			
		||||
            self.assertEqual(w.exitcode, 0)
 | 
			
		||||
        pin_thread.join(1.0)
 | 
			
		||||
        self.assertFalse(pin_thread.is_alive())
 | 
			
		||||
 | 
			
		||||
    def test_len(self):
 | 
			
		||||
        def check_len(dl, expected):
 | 
			
		||||
            self.assertEqual(len(dl), expected)
 | 
			
		||||
            n = 0
 | 
			
		||||
            for sample in dl:
 | 
			
		||||
                n += 1
 | 
			
		||||
            self.assertEqual(n, expected)
 | 
			
		||||
        check_len(self.dataset, 100)
 | 
			
		||||
        check_len(DataLoader(self.dataset, batch_size=2), 50)
 | 
			
		||||
        check_len(DataLoader(self.dataset, batch_size=3), 34)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_NUMPY, "numpy unavailable")
 | 
			
		||||
    def test_numpy_scalars(self):
 | 
			
		||||
        import numpy as np
 | 
			
		||||
 | 
			
		||||
        class ScalarDataset(torch.utils.data.Dataset):
 | 
			
		||||
            def __init__(self, dtype):
 | 
			
		||||
                self.dtype = dtype
 | 
			
		||||
 | 
			
		||||
            def __getitem__(self, i):
 | 
			
		||||
                return self.dtype()
 | 
			
		||||
 | 
			
		||||
            def __len__(self):
 | 
			
		||||
                return 4
 | 
			
		||||
 | 
			
		||||
        dtypes = {
 | 
			
		||||
            np.float64: torch.DoubleTensor,
 | 
			
		||||
            np.float32: torch.FloatTensor,
 | 
			
		||||
            np.float16: torch.HalfTensor,
 | 
			
		||||
            np.int64: torch.LongTensor,
 | 
			
		||||
            np.int32: torch.IntTensor,
 | 
			
		||||
            np.int16: torch.ShortTensor,
 | 
			
		||||
            np.int8: torch.CharTensor,
 | 
			
		||||
            np.uint8: torch.ByteTensor,
 | 
			
		||||
        }
 | 
			
		||||
        for dt, tt in dtypes.items():
 | 
			
		||||
            dset = ScalarDataset(dt)
 | 
			
		||||
            loader = DataLoader(dset, batch_size=2)
 | 
			
		||||
            batch = next(iter(loader))
 | 
			
		||||
            self.assertIsInstance(batch, tt)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StringDataset(Dataset):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.s = '12345'
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return len(self.s)
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, ndx):
 | 
			
		||||
        return (self.s[ndx], ndx)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestStringDataLoader(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.dataset = StringDataset()
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA, "CUDA unavailable")
 | 
			
		||||
    def test_shuffle_pin_memory(self):
 | 
			
		||||
        loader = DataLoader(self.dataset, batch_size=2, shuffle=True, num_workers=4, pin_memory=True)
 | 
			
		||||
        for batch_ndx, (s, n) in enumerate(loader):
 | 
			
		||||
            self.assertIsInstance(s[0], str)
 | 
			
		||||
            self.assertTrue(n.is_pinned())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DictDataset(Dataset):
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return 4
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, ndx):
 | 
			
		||||
        return {
 | 
			
		||||
            'a_tensor': torch.Tensor(4, 2).fill_(ndx),
 | 
			
		||||
            'another_dict': {
 | 
			
		||||
                'a_number': ndx,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDictDataLoader(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.dataset = DictDataset()
 | 
			
		||||
 | 
			
		||||
    def test_sequential_batch(self):
 | 
			
		||||
        loader = DataLoader(self.dataset, batch_size=2, shuffle=False)
 | 
			
		||||
        batch_size = loader.batch_size
 | 
			
		||||
        for i, sample in enumerate(loader):
 | 
			
		||||
            idx = i * batch_size
 | 
			
		||||
            self.assertEqual(set(sample.keys()), {'a_tensor', 'another_dict'})
 | 
			
		||||
            self.assertEqual(set(sample['another_dict'].keys()), {'a_number'})
 | 
			
		||||
 | 
			
		||||
            t = sample['a_tensor']
 | 
			
		||||
            self.assertEqual(t.size(), torch.Size([batch_size, 4, 2]))
 | 
			
		||||
            self.assertTrue((t[0] == idx).all())
 | 
			
		||||
            self.assertTrue((t[1] == idx + 1).all())
 | 
			
		||||
 | 
			
		||||
            n = sample['another_dict']['a_number']
 | 
			
		||||
            self.assertEqual(n.size(), torch.Size([batch_size]))
 | 
			
		||||
            self.assertEqual(n[0], idx)
 | 
			
		||||
            self.assertEqual(n[1], idx + 1)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA, "CUDA unavailable")
 | 
			
		||||
    def test_pin_memory(self):
 | 
			
		||||
        loader = DataLoader(self.dataset, batch_size=2, pin_memory=True)
 | 
			
		||||
        for batch_ndx, sample in enumerate(loader):
 | 
			
		||||
            self.assertTrue(sample['a_tensor'].is_pinned())
 | 
			
		||||
            self.assertTrue(sample['another_dict']['a_number'].is_pinned())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    run_tests()
 | 
			
		||||
							
								
								
									
										548
									
								
								test/test_distributed.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										548
									
								
								test/test_distributed.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,548 @@
 | 
			
		||||
import fcntl
 | 
			
		||||
import multiprocessing
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
import unittest
 | 
			
		||||
from functools import wraps, reduce
 | 
			
		||||
from contextlib import contextmanager
 | 
			
		||||
 | 
			
		||||
import torch
 | 
			
		||||
import torch.distributed as dist
 | 
			
		||||
from common import TestCase
 | 
			
		||||
 | 
			
		||||
BACKEND = os.environ['BACKEND']
 | 
			
		||||
TEMP_DIR = os.environ['TEMP_DIR']
 | 
			
		||||
MASTER_PORT = '29500'
 | 
			
		||||
MASTER_ADDR = '127.0.0.1'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if not dist.is_available():
 | 
			
		||||
    print('Distributed not available, skipping tests')
 | 
			
		||||
    sys.exit(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextmanager
 | 
			
		||||
def _lock():
 | 
			
		||||
    lockfile = os.path.join(TEMP_DIR, 'lockfile')
 | 
			
		||||
    with open(lockfile, 'w') as lf:
 | 
			
		||||
        try:
 | 
			
		||||
            fcntl.flock(lf.fileno(), fcntl.LOCK_EX)
 | 
			
		||||
            yield
 | 
			
		||||
        finally:
 | 
			
		||||
            fcntl.flock(lf.fileno(), fcntl.LOCK_UN)
 | 
			
		||||
            lf.close()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _build_tensor(size, value=None):
 | 
			
		||||
    if value is None:
 | 
			
		||||
        value = size
 | 
			
		||||
    return torch.FloatTensor(size, size, size).fill_(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Barrier(object):
 | 
			
		||||
    barrier_id = 0
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def init(cls):
 | 
			
		||||
        cls.barrier_id = 0
 | 
			
		||||
        barrier_dir = os.path.join(TEMP_DIR, 'barrier')
 | 
			
		||||
        for f_name in os.listdir(barrier_dir):
 | 
			
		||||
            os.unlink(os.path.join(barrier_dir, f_name))
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def sync(cls, timeout=5):
 | 
			
		||||
        cls.barrier_id += 1
 | 
			
		||||
        barrier_dir = os.path.join(TEMP_DIR, 'barrier')
 | 
			
		||||
        pid = str(os.getpid())
 | 
			
		||||
        barrier_file = os.path.join(barrier_dir, pid)
 | 
			
		||||
        with _lock():
 | 
			
		||||
            with open(barrier_file, 'w') as f:
 | 
			
		||||
                f.write(str(cls.barrier_id))
 | 
			
		||||
 | 
			
		||||
        start_time = time.time()
 | 
			
		||||
        while True:
 | 
			
		||||
            arrived = 0
 | 
			
		||||
            with _lock():
 | 
			
		||||
                for f_name in os.listdir(barrier_dir):
 | 
			
		||||
                    with open(os.path.join(barrier_dir, f_name), 'r') as f:
 | 
			
		||||
                        data = f.read()
 | 
			
		||||
                        if int(data) >= cls.barrier_id:
 | 
			
		||||
                            arrived += 1
 | 
			
		||||
            if arrived == dist.get_world_size():
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            if time.time() - start_time > timeout:
 | 
			
		||||
                raise RuntimeError("barrier timeout")
 | 
			
		||||
            time.sleep(0.1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _DistTestBase(object):
 | 
			
		||||
 | 
			
		||||
    def _barrier(self, *args, **kwargs):
 | 
			
		||||
        Barrier.sync(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def _init_group_test(self):
 | 
			
		||||
        group = [1, 2]
 | 
			
		||||
        group_id = dist.new_group(group)
 | 
			
		||||
        rank = dist.get_rank()
 | 
			
		||||
        if rank not in group:
 | 
			
		||||
            return ([], None, rank)
 | 
			
		||||
 | 
			
		||||
        return (group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    def _init_global_test(self):
 | 
			
		||||
        group = [i for i in range(0, dist.get_world_size())]
 | 
			
		||||
        group_id = dist.group.WORLD
 | 
			
		||||
        rank = dist.get_rank()
 | 
			
		||||
        return (group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    # GET RANK
 | 
			
		||||
    def test_get_rank(self):
 | 
			
		||||
        test_dir = os.path.join(TEMP_DIR, 'test_dir')
 | 
			
		||||
        pid = str(os.getpid())
 | 
			
		||||
        num_processes = dist.get_world_size()
 | 
			
		||||
        with open(os.path.join(test_dir, pid), 'w') as f:
 | 
			
		||||
            f.write(str(dist.get_rank()))
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
        all_ranks = set()
 | 
			
		||||
        for f_name in os.listdir(test_dir):
 | 
			
		||||
            with open(os.path.join(test_dir, f_name), 'r') as f:
 | 
			
		||||
                all_ranks.add(int(f.read()))
 | 
			
		||||
        self.assertEqual(len(all_ranks), num_processes)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
        if dist.get_rank() == 0:
 | 
			
		||||
            for f_name in os.listdir(test_dir):
 | 
			
		||||
                os.unlink(os.path.join(test_dir, f_name))
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    # SEND RECV
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support send/recv")
 | 
			
		||||
    def test_send_recv(self):
 | 
			
		||||
        rank = dist.get_rank()
 | 
			
		||||
        tensor = _build_tensor(rank + 1)
 | 
			
		||||
        for dest in range(0, dist.get_world_size()):
 | 
			
		||||
            if dest == rank:
 | 
			
		||||
                continue
 | 
			
		||||
            dist.send(tensor, dest)
 | 
			
		||||
 | 
			
		||||
        for src in range(0, dist.get_world_size()):
 | 
			
		||||
            if src == rank:
 | 
			
		||||
                continue
 | 
			
		||||
            tensor = _build_tensor(src + 1, value=-1)
 | 
			
		||||
            expected_tensor = _build_tensor(src + 1)
 | 
			
		||||
            dist.recv(tensor, src)
 | 
			
		||||
            self.assertEqual(tensor, expected_tensor)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    # SEND RECV ANY SOURCE
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo',
 | 
			
		||||
                     "Gloo does not support send/recv from any source")
 | 
			
		||||
    def test_send_recv_any_source(self):
 | 
			
		||||
        rank = dist.get_rank()
 | 
			
		||||
        tensor = _build_tensor(10, rank)
 | 
			
		||||
        for dest in range(0, dist.get_world_size()):
 | 
			
		||||
            if dest == rank:
 | 
			
		||||
                continue
 | 
			
		||||
            dist.send(tensor, dest)
 | 
			
		||||
 | 
			
		||||
        recv_ranks = set()
 | 
			
		||||
        for src in range(0, dist.get_world_size()):
 | 
			
		||||
            if src == rank:
 | 
			
		||||
                continue
 | 
			
		||||
            tensor = _build_tensor(10, value=-1)
 | 
			
		||||
            dist.recv(tensor)
 | 
			
		||||
            recv_ranks.add(tensor.resize_(1)[0])
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(len(recv_ranks), dist.get_world_size() - 1)
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    # ISEND
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support isend")
 | 
			
		||||
    def test_isend(self):
 | 
			
		||||
        rank = dist.get_rank()
 | 
			
		||||
        world_size = dist.get_world_size()
 | 
			
		||||
 | 
			
		||||
        if rank == 0:
 | 
			
		||||
            requests = [
 | 
			
		||||
                dist.isend(_build_tensor(dest, 10), dest) for dest in range(1, world_size)
 | 
			
		||||
            ]
 | 
			
		||||
            for request in requests:
 | 
			
		||||
                request.wait()
 | 
			
		||||
                self.assertTrue(request.is_completed())
 | 
			
		||||
        else:
 | 
			
		||||
            tensor = _build_tensor(rank, -1)
 | 
			
		||||
            dist.recv(tensor, 0)
 | 
			
		||||
            self.assertEqual(tensor, _build_tensor(rank, 10))
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    # IRECV
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support irecv")
 | 
			
		||||
    def test_irecv(self):
 | 
			
		||||
        rank = dist.get_rank()
 | 
			
		||||
        world_size = dist.get_world_size()
 | 
			
		||||
 | 
			
		||||
        if rank == 0:
 | 
			
		||||
            expected_tensors = [_build_tensor(src, -1) for src in range(1, world_size)]
 | 
			
		||||
            requests = [
 | 
			
		||||
                dist.irecv(expected_tensors[src - 1], src) for src in range(1, world_size)
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
            for src in range(1, world_size):
 | 
			
		||||
                requests[src - 1].wait()
 | 
			
		||||
                self.assertTrue(requests[src - 1].is_completed())
 | 
			
		||||
                self.assertEqual(expected_tensors[src - 1], _build_tensor(src, 10))
 | 
			
		||||
        else:
 | 
			
		||||
            tensor = _build_tensor(rank, 10)
 | 
			
		||||
            dist.send(tensor, 0)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    # BROADCAST
 | 
			
		||||
    def _test_broadcast_helper(self, group, group_id, rank, cuda=False):
 | 
			
		||||
        for src in group:
 | 
			
		||||
            expected_tensor = _build_tensor(src + 1)
 | 
			
		||||
            if cuda:
 | 
			
		||||
                expected_tensor = expected_tensor.cuda()
 | 
			
		||||
            if rank == src:
 | 
			
		||||
                dist.broadcast(expected_tensor, src, group_id)
 | 
			
		||||
            else:
 | 
			
		||||
                tensor = _build_tensor(src + 1, -1)
 | 
			
		||||
                if cuda:
 | 
			
		||||
                    tensor = tensor.cuda()
 | 
			
		||||
                dist.broadcast(tensor, src, group_id)
 | 
			
		||||
                self.assertEqual(tensor, expected_tensor)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    def test_broadcast(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_broadcast_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND != 'gloo', "Only Gloo backend supports CUDA allReduce")
 | 
			
		||||
    def test_broadcast_cuda(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_broadcast_helper(group, group_id, rank, True)
 | 
			
		||||
 | 
			
		||||
    def test_broadcast_group(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_broadcast_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    # REDUCE
 | 
			
		||||
    def _test_reduce_helper(self, group, group_id, rank, op, master_value, worker_value, expected_value):
 | 
			
		||||
        for src in group:
 | 
			
		||||
            if rank == src:
 | 
			
		||||
                tensor = _build_tensor(src + 1).fill_(master_value)
 | 
			
		||||
                dist.reduce(tensor, src, op, group_id)
 | 
			
		||||
                self.assertEqual(tensor, _build_tensor(src + 1, expected_value))
 | 
			
		||||
            else:
 | 
			
		||||
                tensor = _build_tensor(src + 1).fill_(worker_value)
 | 
			
		||||
                dist.reduce(tensor, src, op, group_id)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_sum(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.SUM, 2, 10, 2 + (10 * (len(group) - 1))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_product(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.PRODUCT,
 | 
			
		||||
            2, 10, reduce((lambda x, y: x * y), [10] * (len(group) - 1), 2)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_min(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MIN, 1010, 1, 1
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_max(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MAX, -1, 10, 10
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_group_sum(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.SUM, 2, 10, 2 + (10 * (len(group) - 1))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_group_product(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.PRODUCT,
 | 
			
		||||
            2, 10, reduce((lambda x, y: x * y), [10] * (len(group) - 1), 2)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_group_min(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MIN, 1010, 1, 1
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support reduce")
 | 
			
		||||
    def test_reduce_group_max(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MAX, -1, 10, 10
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # ALL REDUCE
 | 
			
		||||
    def _test_all_reduce_helper(self, group, group_id, rank, op, master_value,
 | 
			
		||||
                                worker_value, expected_value, cuda=False):
 | 
			
		||||
        for src in group:
 | 
			
		||||
            if rank == src:
 | 
			
		||||
                tensor = _build_tensor(src + 1).fill_(master_value)
 | 
			
		||||
                if cuda:
 | 
			
		||||
                    tensor = tensor.cuda()
 | 
			
		||||
                dist.all_reduce(tensor, op, group_id)
 | 
			
		||||
                self.assertEqual(tensor, _build_tensor(src + 1, expected_value))
 | 
			
		||||
            else:
 | 
			
		||||
                tensor = _build_tensor(src + 1).fill_(worker_value)
 | 
			
		||||
                if cuda:
 | 
			
		||||
                    tensor = tensor.cuda()
 | 
			
		||||
                dist.all_reduce(tensor, op, group_id)
 | 
			
		||||
                self.assertEqual(tensor, _build_tensor(src + 1, expected_value))
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_sum(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.SUM, 2, 10, 2 + (10 * (len(group) - 1))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND != 'gloo', "Only Gloo backend supports CUDA allReduce")
 | 
			
		||||
    def test_all_reduce_sum_cuda(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.SUM, 2, 10, 2 + (10 * (len(group) - 1)), True
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_product(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.PRODUCT,
 | 
			
		||||
            2, 10, reduce((lambda x, y: x * y), [10] * (len(group) - 1), 2)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_min(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MIN, 1010, 1, 1
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_max(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MAX, -1, 10, 10
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_group_sum(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.SUM, 2, 10, 2 + (10 * (len(group) - 1))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_group_product(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.PRODUCT,
 | 
			
		||||
            2, 10, reduce((lambda x, y: x * y), [10] * (len(group) - 1), 2)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_group_min(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MIN, 1010, 1, 1
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_all_reduce_group_max(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_all_reduce_helper(
 | 
			
		||||
            group, group_id, rank, dist.reduce_op.MAX, -1, 10, 10
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # SCATTER
 | 
			
		||||
    def _test_scatter_helper(self, group, group_id, rank):
 | 
			
		||||
        for dest in group:
 | 
			
		||||
            tensor = _build_tensor(dest + 1, -1)
 | 
			
		||||
            expected_tensor = _build_tensor(dest + 1, rank)
 | 
			
		||||
            tensors = [_build_tensor(dest + 1, i) for i in group] if rank == dest else []
 | 
			
		||||
            dist.scatter(tensor, src=dest, scatter_list=tensors, group=group_id)
 | 
			
		||||
            self.assertEqual(tensor, expected_tensor)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support scatter")
 | 
			
		||||
    def test_scatter(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_scatter_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support scatter")
 | 
			
		||||
    def test_scatter_group(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_scatter_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    # GATHER
 | 
			
		||||
    def _test_gather_helper(self, group, group_id, rank):
 | 
			
		||||
        for dest in group:
 | 
			
		||||
            tensor = _build_tensor(dest + 1, rank)
 | 
			
		||||
            tensors = [_build_tensor(dest + 1, -1) for i in group] if rank == dest else []
 | 
			
		||||
            dist.gather(tensor, dst=dest, gather_list=tensors, group=group_id)
 | 
			
		||||
            if rank == dest:
 | 
			
		||||
                expected_tensors = [_build_tensor(dest + 1, i) for i in group]
 | 
			
		||||
                for t1, t2 in zip(tensors, expected_tensors):
 | 
			
		||||
                    self.assertEqual(t1, t2)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support gather")
 | 
			
		||||
    def test_gather(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_gather_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(BACKEND == 'gloo', "Gloo does not support gather")
 | 
			
		||||
    def test_gather_group(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_gather_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    # ALL GATHER
 | 
			
		||||
    def _test_all_gather_helper(self, group, group_id, rank):
 | 
			
		||||
        for dest in group:
 | 
			
		||||
            tensor = _build_tensor(dest + 1, rank)
 | 
			
		||||
            tensors = [_build_tensor(dest + 1, -1) for i in group]
 | 
			
		||||
            dist.all_gather(tensors, tensor, group_id)
 | 
			
		||||
 | 
			
		||||
            expected_tensors = [_build_tensor(dest + 1, i) for i in group]
 | 
			
		||||
            for t1, t2 in zip(tensors, expected_tensors):
 | 
			
		||||
                self.assertEqual(t1, t2)
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    def test_all_gather(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_all_gather_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    def test_all_gather_group(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_all_gather_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    # BARRIER
 | 
			
		||||
    def _test_barrier_helper(self, group, group_id, rank):
 | 
			
		||||
        WAIT_TIME = 0.3  # seconds
 | 
			
		||||
 | 
			
		||||
        for dest in group:
 | 
			
		||||
            expected_time = torch.DoubleTensor(1).fill_(0.0)
 | 
			
		||||
            if dest == rank:
 | 
			
		||||
                expected_time.fill_(time.time() + WAIT_TIME)
 | 
			
		||||
                dist.broadcast(expected_time, dest, group_id)
 | 
			
		||||
                time.sleep(WAIT_TIME + 0.1)  # sleep a little bit longer
 | 
			
		||||
                dist.barrier(group_id)
 | 
			
		||||
            else:
 | 
			
		||||
                dist.broadcast(expected_time, dest, group_id)
 | 
			
		||||
                dist.barrier(group_id)
 | 
			
		||||
                self.assertGreaterEqual(time.time(), expected_time[0])
 | 
			
		||||
 | 
			
		||||
        self._barrier()
 | 
			
		||||
 | 
			
		||||
    def test_barrier(self):
 | 
			
		||||
        group, group_id, rank = self._init_global_test()
 | 
			
		||||
        self._test_barrier_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
    def test_barrier_group(self):
 | 
			
		||||
        group, group_id, rank = self._init_group_test()
 | 
			
		||||
        self._test_barrier_helper(group, group_id, rank)
 | 
			
		||||
 | 
			
		||||
if BACKEND == 'tcp' or BACKEND == 'gloo':
 | 
			
		||||
    WORLD_SIZE = os.environ['WORLD_SIZE']
 | 
			
		||||
 | 
			
		||||
    class TestTCPOrGloo(TestCase, _DistTestBase):
 | 
			
		||||
 | 
			
		||||
        MANAGER_PROCESS_RANK = -1
 | 
			
		||||
        JOIN_TIMEOUT = 10
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def manager_join(fn):
 | 
			
		||||
            @wraps(fn)
 | 
			
		||||
            def wrapper(self):
 | 
			
		||||
                if self.rank == self.MANAGER_PROCESS_RANK:
 | 
			
		||||
                    self._join_and_reduce()
 | 
			
		||||
                else:
 | 
			
		||||
                    fn(self)
 | 
			
		||||
            return wrapper
 | 
			
		||||
 | 
			
		||||
        @classmethod
 | 
			
		||||
        def setUpClass(cls):
 | 
			
		||||
            os.environ['MASTER_ADDR'] = MASTER_ADDR
 | 
			
		||||
            os.environ['MASTER_PORT'] = MASTER_PORT
 | 
			
		||||
            os.environ['WORLD_SIZE'] = WORLD_SIZE
 | 
			
		||||
            for attr in dir(cls):
 | 
			
		||||
                if attr.startswith('test'):
 | 
			
		||||
                    fn = getattr(cls, attr)
 | 
			
		||||
                    setattr(cls, attr, cls.manager_join(fn))
 | 
			
		||||
 | 
			
		||||
        def setUp(self):
 | 
			
		||||
            self.processes = []
 | 
			
		||||
            self.rank = self.MANAGER_PROCESS_RANK
 | 
			
		||||
            Barrier.init()
 | 
			
		||||
            for rank in range(int(WORLD_SIZE)):
 | 
			
		||||
                self.processes.append(self._spawn_process(rank))
 | 
			
		||||
 | 
			
		||||
        def tearDown(self):
 | 
			
		||||
            for p in self.processes:
 | 
			
		||||
                p.terminate()
 | 
			
		||||
 | 
			
		||||
        def _spawn_process(self, rank):
 | 
			
		||||
            os.environ['RANK'] = str(rank)
 | 
			
		||||
            name = 'process ' + str(rank)
 | 
			
		||||
            process = multiprocessing.Process(target=self._run, name=name,
 | 
			
		||||
                                              args=(rank,))
 | 
			
		||||
            process.start()
 | 
			
		||||
            return process
 | 
			
		||||
 | 
			
		||||
        def _run(self, rank):
 | 
			
		||||
            self.rank = rank
 | 
			
		||||
            try:
 | 
			
		||||
                dist.init_process_group(backend=BACKEND)
 | 
			
		||||
            except RuntimeError as e:
 | 
			
		||||
                if 'recompile' in e.args[0]:
 | 
			
		||||
                    sys.exit(0)
 | 
			
		||||
            # self.id() == e.g. '__main__.TestDistributed.test_get_rank'
 | 
			
		||||
            # We're retreiving a corresponding test and executing it.
 | 
			
		||||
            getattr(self, self.id().split(".")[2])()
 | 
			
		||||
            sys.exit(0)
 | 
			
		||||
 | 
			
		||||
        def _join_and_reduce(self):
 | 
			
		||||
            for p in self.processes:
 | 
			
		||||
                p.join(self.JOIN_TIMEOUT)
 | 
			
		||||
                self.assertEqual(p.exitcode, 0)
 | 
			
		||||
 | 
			
		||||
elif BACKEND == 'mpi':
 | 
			
		||||
    dist.init_process_group(backend='mpi')
 | 
			
		||||
 | 
			
		||||
    class TestMPI(TestCase, _DistTestBase):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
							
								
								
									
										1258
									
								
								test/test_legacy_nn.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1258
									
								
								test/test_legacy_nn.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										421
									
								
								test/test_multiprocessing.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										421
									
								
								test/test_multiprocessing.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,421 @@
 | 
			
		||||
import contextlib
 | 
			
		||||
import gc
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
import unittest
 | 
			
		||||
from sys import platform
 | 
			
		||||
 | 
			
		||||
import torch
 | 
			
		||||
import torch.cuda
 | 
			
		||||
import torch.multiprocessing as mp
 | 
			
		||||
from torch.autograd import Variable
 | 
			
		||||
from torch.nn import Parameter
 | 
			
		||||
from common import TestCase, run_tests
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_REPEATS = 30
 | 
			
		||||
HAS_SHM_FILES = os.path.isdir('/dev/shm')
 | 
			
		||||
TEST_CUDA_IPC = torch.cuda.is_available() and \
 | 
			
		||||
    sys.version_info[0] == 3 and \
 | 
			
		||||
    sys.platform != 'darwin'
 | 
			
		||||
TEST_MULTIGPU = TEST_CUDA_IPC and torch.cuda.device_count() > 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def simple_fill(queue, event):
 | 
			
		||||
    data = queue.get()
 | 
			
		||||
    data[0][:] = 4
 | 
			
		||||
    event.set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def simple_pool_fill(tensor):
 | 
			
		||||
    tensor.fill_(4)
 | 
			
		||||
    return tensor.add(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def send_tensor(queue, event, tp):
 | 
			
		||||
    t = torch.ones(5, 5).type(tp)
 | 
			
		||||
    queue.put(t)
 | 
			
		||||
    queue.put(t)
 | 
			
		||||
    event.wait()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sum_tensors(inq, outq):
 | 
			
		||||
    with torch.cuda.device(1):
 | 
			
		||||
        tensors = inq.get()
 | 
			
		||||
        for tensor in tensors:
 | 
			
		||||
            outq.put((tensor.sum(), tensor.get_device(),
 | 
			
		||||
                      tensor.numel(), tensor.storage().size()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def queue_get_exception(inqueue, outqueue):
 | 
			
		||||
    os.close(2)  # hide expected error message
 | 
			
		||||
    try:
 | 
			
		||||
        torch.zeros(5, 5).cuda()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        outqueue.put(e)
 | 
			
		||||
    else:
 | 
			
		||||
        outqueue.put('no exception')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Multiply by two in a separate stream
 | 
			
		||||
def cuda_multiply_two(queue, ready, done):
 | 
			
		||||
    ready.set()
 | 
			
		||||
    with torch.cuda.stream(torch.cuda.Stream()):
 | 
			
		||||
        cuda_event, tensor = queue.get()
 | 
			
		||||
        cuda_event.wait()
 | 
			
		||||
        tensor.mul_(2)
 | 
			
		||||
        cuda_event.record()
 | 
			
		||||
        done.set()
 | 
			
		||||
        del cuda_event
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def autograd_sharing(queue, ready, master_modified):
 | 
			
		||||
    var = queue.get()
 | 
			
		||||
    ready.set()
 | 
			
		||||
    master_modified.wait()
 | 
			
		||||
 | 
			
		||||
    expected_var = torch.arange(1, 26).view(5, 5)
 | 
			
		||||
    expected_var[0, 0] = 1000
 | 
			
		||||
    is_ok = var.data.equal(expected_var)
 | 
			
		||||
    var.data[:] = torch.ones(5, 5)
 | 
			
		||||
 | 
			
		||||
    is_ok &= var.grad is None
 | 
			
		||||
    var._grad = Variable(torch.ones(5, 5), requires_grad=False)
 | 
			
		||||
 | 
			
		||||
    queue.put(is_ok)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextlib.contextmanager
 | 
			
		||||
def fs_sharing():
 | 
			
		||||
    prev_strategy = mp.get_sharing_strategy()
 | 
			
		||||
    mp.set_sharing_strategy('file_system')
 | 
			
		||||
    try:
 | 
			
		||||
        yield
 | 
			
		||||
    finally:
 | 
			
		||||
        mp.set_sharing_strategy(prev_strategy)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class leak_checker(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, test_case):
 | 
			
		||||
        self.checked_pids = [os.getpid()]
 | 
			
		||||
        self.test_case = test_case
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        self.next_fds = self._get_next_fds(10)
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
    def __exit__(self, *args):
 | 
			
		||||
        if args[0] is None:
 | 
			
		||||
            # Check that the 10th available file-descriptor at the end of the
 | 
			
		||||
            # test is no more than 4 higher than the 10th available at the
 | 
			
		||||
            # start. This attempts to catch file descriptor leaks, but allows
 | 
			
		||||
            # one-off initialization that may use up a file descriptor
 | 
			
		||||
            # TODO: Disabled because this check is too flaky
 | 
			
		||||
            # available_fds = self._get_next_fds(10)
 | 
			
		||||
            # self.test_case.assertLessEqual(
 | 
			
		||||
            #     available_fds[-1] - self.next_fds[-1], 5)
 | 
			
		||||
            self.test_case.assertFalse(self.has_shm_files())
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def check_pid(self, pid):
 | 
			
		||||
        self.checked_pids.append(pid)
 | 
			
		||||
 | 
			
		||||
    def _get_next_fds(self, n=1):
 | 
			
		||||
        # dup uses the lowest-numbered unused descriptor for the new descriptor
 | 
			
		||||
        fds = [os.dup(0) for i in range(n)]
 | 
			
		||||
        for fd in fds:
 | 
			
		||||
            os.close(fd)
 | 
			
		||||
        return fds
 | 
			
		||||
 | 
			
		||||
    def has_shm_files(self, wait=True):
 | 
			
		||||
        if not HAS_SHM_FILES:
 | 
			
		||||
            return False
 | 
			
		||||
        result = self._has_shm_files()
 | 
			
		||||
        if result and mp.get_sharing_strategy() == 'file_system' and wait:
 | 
			
		||||
            time.sleep(0.5)
 | 
			
		||||
            return self._has_shm_files()
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def _has_shm_files(self):
 | 
			
		||||
        gc.collect()
 | 
			
		||||
        names = list('torch_' + str(pid) for pid in self.checked_pids)
 | 
			
		||||
        for filename in os.listdir('/dev/shm'):
 | 
			
		||||
            for name in names:
 | 
			
		||||
                if filename.startswith(name):
 | 
			
		||||
                    return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestMultiprocessing(TestCase):
 | 
			
		||||
 | 
			
		||||
    def _test_sharing(self, ctx=mp, type=torch.FloatTensor, repeat=1):
 | 
			
		||||
        def test_fill():
 | 
			
		||||
            x = torch.zeros(5, 5).type(type)
 | 
			
		||||
            q = ctx.Queue()
 | 
			
		||||
            e = ctx.Event()
 | 
			
		||||
            data = [x, x[:, 1]]
 | 
			
		||||
            q.put(data)
 | 
			
		||||
            p = ctx.Process(target=simple_fill, args=(q, e))
 | 
			
		||||
            p.daemon = True
 | 
			
		||||
            lc.check_pid(p.pid)
 | 
			
		||||
            p.start()
 | 
			
		||||
            e.wait(10)
 | 
			
		||||
            self.assertTrue(e.is_set())
 | 
			
		||||
            self.assertTrue(data[0].eq(4).all())
 | 
			
		||||
            self.assertTrue(data[1].eq(4).all())
 | 
			
		||||
            p.join(1)
 | 
			
		||||
            self.assertFalse(p.is_alive())
 | 
			
		||||
 | 
			
		||||
        def test_receive():
 | 
			
		||||
            q = ctx.Queue()
 | 
			
		||||
            e = ctx.Event()
 | 
			
		||||
            p = ctx.Process(target=send_tensor, args=(q, e, type))
 | 
			
		||||
            p.daemon = True
 | 
			
		||||
            lc.check_pid(p.pid)
 | 
			
		||||
            p.start()
 | 
			
		||||
            t1 = q.get()
 | 
			
		||||
            t2 = q.get()
 | 
			
		||||
            self.assertTrue(t1.eq(1).all())
 | 
			
		||||
            self.assertTrue(id(t1.storage()) == id(t2.storage()))
 | 
			
		||||
            e.set()
 | 
			
		||||
            p.join(1)
 | 
			
		||||
            self.assertFalse(p.is_alive())
 | 
			
		||||
 | 
			
		||||
        with leak_checker(self) as lc:
 | 
			
		||||
            for _ in range(repeat):
 | 
			
		||||
                test_fill()
 | 
			
		||||
                test_receive()
 | 
			
		||||
 | 
			
		||||
    def _test_preserve_sharing(self, ctx=mp, repeat=1):
 | 
			
		||||
        def do_test():
 | 
			
		||||
            x = torch.randn(5, 5)
 | 
			
		||||
            data = [x.storage(), x.storage()[1:4], x, x[2], x[:, 1]]
 | 
			
		||||
            q = ctx.Queue()
 | 
			
		||||
            q.put(data)
 | 
			
		||||
            new_data = q.get(timeout=1)
 | 
			
		||||
            self.assertEqual(new_data, data, 0)
 | 
			
		||||
            storage_cdata = data[0]._cdata
 | 
			
		||||
            self.assertEqual(new_data[0]._cdata, storage_cdata)
 | 
			
		||||
            for t in new_data[2:]:
 | 
			
		||||
                self.assertEqual(t.storage()._cdata, storage_cdata)
 | 
			
		||||
            # TODO: enable after fixing #46
 | 
			
		||||
            # new_data[0].fill_(10)
 | 
			
		||||
            # self.assertEqual(new_data[1], new_data[0][1:4], 0)
 | 
			
		||||
 | 
			
		||||
        with leak_checker(self):
 | 
			
		||||
            for i in range(repeat):
 | 
			
		||||
                do_test()
 | 
			
		||||
 | 
			
		||||
    def _test_pool(self, ctx=mp, repeat=1):
 | 
			
		||||
        def do_test():
 | 
			
		||||
            p = ctx.Pool(2)
 | 
			
		||||
            for proc in p._pool:
 | 
			
		||||
                lc.check_pid(proc.pid)
 | 
			
		||||
 | 
			
		||||
            buffers = [torch.zeros(2, 2) for i in range(4)]
 | 
			
		||||
            results = p.map(simple_pool_fill, buffers, 1)
 | 
			
		||||
            self.assertEqual(len(results), len(buffers))
 | 
			
		||||
            for r in results:
 | 
			
		||||
                self.assertEqual(r, torch.ones(2, 2) * 5, 0)
 | 
			
		||||
            for b in buffers:
 | 
			
		||||
                self.assertEqual(b, torch.ones(2, 2) * 4, 0)
 | 
			
		||||
 | 
			
		||||
            p.close()
 | 
			
		||||
            p.join()
 | 
			
		||||
 | 
			
		||||
        with leak_checker(self) as lc:
 | 
			
		||||
            for i in range(repeat):
 | 
			
		||||
                do_test()
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(platform == 'darwin', "file descriptor strategy is not supported on OS X")
 | 
			
		||||
    def test_fd_sharing(self):
 | 
			
		||||
        self._test_sharing(repeat=TEST_REPEATS)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(platform == 'darwin', "file descriptor strategy is not supported on OS X")
 | 
			
		||||
    def test_fd_preserve_sharing(self):
 | 
			
		||||
        self._test_preserve_sharing(repeat=TEST_REPEATS)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(platform == 'darwin', "file descriptor strategy is not supported on OS X")
 | 
			
		||||
    def test_fd_pool(self):
 | 
			
		||||
        self._test_pool(repeat=TEST_REPEATS)
 | 
			
		||||
 | 
			
		||||
    def test_fs_sharing(self):
 | 
			
		||||
        with fs_sharing():
 | 
			
		||||
            self._test_sharing(repeat=TEST_REPEATS)
 | 
			
		||||
 | 
			
		||||
    def test_fs_preserve_sharing(self):
 | 
			
		||||
        with fs_sharing():
 | 
			
		||||
            self._test_preserve_sharing(repeat=TEST_REPEATS)
 | 
			
		||||
 | 
			
		||||
    def test_fs_pool(self):
 | 
			
		||||
        with fs_sharing():
 | 
			
		||||
            self._test_pool(repeat=TEST_REPEATS)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not HAS_SHM_FILES, "don't not how to check if shm files exist")
 | 
			
		||||
    def test_fs(self):
 | 
			
		||||
        def queue_put():
 | 
			
		||||
            x = torch.DoubleStorage(4)
 | 
			
		||||
            q = mp.Queue()
 | 
			
		||||
            self.assertFalse(lc.has_shm_files())
 | 
			
		||||
            q.put(x)
 | 
			
		||||
            time.sleep(0.05)  # queue serializes asynchronously
 | 
			
		||||
            self.assertTrue(lc.has_shm_files(wait=False))
 | 
			
		||||
            q.get()
 | 
			
		||||
 | 
			
		||||
        with fs_sharing(), leak_checker(self) as lc:
 | 
			
		||||
            for _ in range(TEST_REPEATS):
 | 
			
		||||
                queue_put()
 | 
			
		||||
 | 
			
		||||
    def test_inherit_tensor(self):
 | 
			
		||||
        class SubProcess(mp.Process):
 | 
			
		||||
            def __init__(self, tensor):
 | 
			
		||||
                super(SubProcess, self).__init__()
 | 
			
		||||
                self.tensor = tensor
 | 
			
		||||
                self.daemon = True
 | 
			
		||||
 | 
			
		||||
            def run(self):
 | 
			
		||||
                self.tensor.add_(3)
 | 
			
		||||
 | 
			
		||||
        t = torch.zeros(5, 5)
 | 
			
		||||
        p = SubProcess(t.share_memory_())
 | 
			
		||||
        p.start()
 | 
			
		||||
        p.join(1)
 | 
			
		||||
        self.assertEqual(t, torch.ones(5, 5) * 3, 0)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA_IPC, 'CUDA IPC not available')
 | 
			
		||||
    def test_cuda(self):
 | 
			
		||||
        torch.cuda.FloatTensor([1])  # initialize CUDA outside of leak checker
 | 
			
		||||
        self._test_sharing(mp.get_context('spawn'), torch.cuda.FloatTensor)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA_IPC, 'CUDA IPC not available')
 | 
			
		||||
    @unittest.skipIf(not TEST_MULTIGPU, 'found only 1 GPU')
 | 
			
		||||
    def test_cuda_small_tensors(self):
 | 
			
		||||
        # Check multiple small tensors which will likely use the same
 | 
			
		||||
        # underlying cached allocation
 | 
			
		||||
        ctx = mp.get_context('spawn')
 | 
			
		||||
        tensors = []
 | 
			
		||||
        for i in range(5):
 | 
			
		||||
            device = i % 2
 | 
			
		||||
            tensors += [torch.arange(i * 5, (i + 1) * 5).cuda(device)]
 | 
			
		||||
 | 
			
		||||
        inq = ctx.Queue()
 | 
			
		||||
        outq = ctx.Queue()
 | 
			
		||||
        inq.put(tensors)
 | 
			
		||||
        p = ctx.Process(target=sum_tensors, args=(inq, outq))
 | 
			
		||||
        p.start()
 | 
			
		||||
 | 
			
		||||
        results = []
 | 
			
		||||
        for i in range(5):
 | 
			
		||||
            results.append(outq.get())
 | 
			
		||||
        p.join()
 | 
			
		||||
 | 
			
		||||
        for i, tensor in enumerate(tensors):
 | 
			
		||||
            v, device, tensor_size, storage_size = results[i]
 | 
			
		||||
            self.assertEqual(v, torch.arange(i * 5, (i + 1) * 5).sum())
 | 
			
		||||
            self.assertEqual(device, i % 2)
 | 
			
		||||
            self.assertEqual(tensor_size, 5)
 | 
			
		||||
            self.assertEqual(storage_size, 5)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not torch.cuda.is_available(), 'CUDA not available')
 | 
			
		||||
    def test_cuda_bad_call(self):
 | 
			
		||||
        # Initialize CUDA
 | 
			
		||||
        t = torch.zeros(5, 5).cuda().cpu()
 | 
			
		||||
        inq = mp.Queue()
 | 
			
		||||
        outq = mp.Queue()
 | 
			
		||||
        p = mp.Process(target=queue_get_exception, args=(inq, outq))
 | 
			
		||||
        p.start()
 | 
			
		||||
        inq.put(t)
 | 
			
		||||
        p.join()
 | 
			
		||||
        self.assertIsInstance(outq.get(), RuntimeError)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not TEST_CUDA_IPC, 'CUDA IPC not available')
 | 
			
		||||
    def test_event(self):
 | 
			
		||||
        ctx = mp.get_context('spawn')
 | 
			
		||||
        queue = ctx.Queue()
 | 
			
		||||
        ready = ctx.Event()
 | 
			
		||||
        done = ctx.Event()
 | 
			
		||||
        p = ctx.Process(target=cuda_multiply_two, args=(queue, ready, done))
 | 
			
		||||
        p.start()
 | 
			
		||||
 | 
			
		||||
        ready.wait()
 | 
			
		||||
        with torch.cuda.stream(torch.cuda.Stream()):
 | 
			
		||||
            tensor = torch.cuda.FloatTensor([1, 1, 1, 1])
 | 
			
		||||
            # Use a sleep kernel to test events. Without the event, the
 | 
			
		||||
            # multiply happens before the add.
 | 
			
		||||
            event = torch.cuda.Event(interprocess=True)
 | 
			
		||||
            torch.cuda._sleep(20000000)  # about 30 ms
 | 
			
		||||
            tensor.add_(1)
 | 
			
		||||
            event.record()
 | 
			
		||||
            queue.put((event, tensor))
 | 
			
		||||
            done.wait()  # must wait until subprocess records event
 | 
			
		||||
            event.synchronize()
 | 
			
		||||
            self.assertEqual(list(tensor), [4, 4, 4, 4])
 | 
			
		||||
        p.join()
 | 
			
		||||
 | 
			
		||||
    def _test_autograd_sharing(self, var):
 | 
			
		||||
        ready = mp.Event()
 | 
			
		||||
        master_modified = mp.Event()
 | 
			
		||||
        queue = mp.Queue()
 | 
			
		||||
        p = mp.Process(target=autograd_sharing, args=(queue, ready, master_modified))
 | 
			
		||||
        p.daemon = True
 | 
			
		||||
        p.start()
 | 
			
		||||
        var._grad = Variable(torch.zeros(5, 5), requires_grad=False)
 | 
			
		||||
        queue.put(var)
 | 
			
		||||
 | 
			
		||||
        ready.wait()
 | 
			
		||||
        var.data[0, 0] = 1000
 | 
			
		||||
        var.grad.data[:] = torch.ones(5, 5) * 4
 | 
			
		||||
        master_modified.set()
 | 
			
		||||
 | 
			
		||||
        worker_ok = queue.get()
 | 
			
		||||
        self.assertTrue(worker_ok)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(var.data, torch.ones(5, 5))
 | 
			
		||||
        self.assertEqual(var.grad.data, torch.ones(5, 5) * 4)
 | 
			
		||||
        p.join(1)
 | 
			
		||||
        self.assertFalse(p.is_alive())
 | 
			
		||||
 | 
			
		||||
    def test_variable_sharing(self):
 | 
			
		||||
        configs = [
 | 
			
		||||
            (True, False),
 | 
			
		||||
            (False, False),
 | 
			
		||||
            (False, True),
 | 
			
		||||
        ]
 | 
			
		||||
        for requires_grad, volatile in configs:
 | 
			
		||||
            var = Variable(torch.arange(1, 26).view(5, 5),
 | 
			
		||||
                           requires_grad=requires_grad,
 | 
			
		||||
                           volatile=volatile)
 | 
			
		||||
            self._test_autograd_sharing(var)
 | 
			
		||||
 | 
			
		||||
    def test_parameter_sharing(self):
 | 
			
		||||
        param = Parameter(torch.arange(1, 26).view(5, 5))
 | 
			
		||||
        self._test_autograd_sharing(param)
 | 
			
		||||
 | 
			
		||||
    def test_empty_shared(self):
 | 
			
		||||
        t = torch.Tensor()
 | 
			
		||||
        t.share_memory_()
 | 
			
		||||
 | 
			
		||||
    def _test_is_shared(self):
 | 
			
		||||
        t = torch.randn(5, 5)
 | 
			
		||||
        self.assertFalse(t.is_shared())
 | 
			
		||||
        t.share_memory_()
 | 
			
		||||
        self.assertTrue(t.is_shared())
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(platform == 'darwin', "file descriptor strategy is not supported on OS X")
 | 
			
		||||
    def test_is_shared(self):
 | 
			
		||||
        self._test_is_shared()
 | 
			
		||||
 | 
			
		||||
    def test_fs_is_shared(self):
 | 
			
		||||
        with fs_sharing():
 | 
			
		||||
            self._test_is_shared()
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not torch.cuda.is_available(), 'CUDA not available')
 | 
			
		||||
    def test_is_shared_cuda(self):
 | 
			
		||||
        t = torch.randn(5, 5).cuda()
 | 
			
		||||
        self.assertTrue(t.is_shared())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    run_tests()
 | 
			
		||||
							
								
								
									
										88
									
								
								test/test_nccl.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								test/test_nccl.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
import torch
 | 
			
		||||
import torch.cuda.nccl as nccl
 | 
			
		||||
import torch.cuda
 | 
			
		||||
 | 
			
		||||
from common import TestCase, run_tests
 | 
			
		||||
 | 
			
		||||
nGPUs = torch.cuda.device_count()
 | 
			
		||||
if nGPUs == 0:
 | 
			
		||||
    print('CUDA not available, skipping tests')
 | 
			
		||||
    TestCase = object  # noqa: F811
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestNCCL(TestCase):
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(nGPUs < 2, "only one GPU detected")
 | 
			
		||||
    def test_broadcast(self):
 | 
			
		||||
        expected = torch.FloatTensor(128).uniform_()
 | 
			
		||||
        tensors = [expected.cuda()]
 | 
			
		||||
        for device in range(1, torch.cuda.device_count()):
 | 
			
		||||
            with torch.cuda.device(device):
 | 
			
		||||
                tensors.append(torch.cuda.FloatTensor(128))
 | 
			
		||||
 | 
			
		||||
        nccl.broadcast(tensors)
 | 
			
		||||
        for i in range(torch.cuda.device_count()):
 | 
			
		||||
            self.assertEqual(tensors[i], expected)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(nGPUs < 2, "only one GPU detected")
 | 
			
		||||
    def test_reduce(self):
 | 
			
		||||
        tensors = [torch.FloatTensor(128).uniform_() for i in range(nGPUs)]
 | 
			
		||||
        expected = torch.FloatTensor(128).zero_()
 | 
			
		||||
        for t in tensors:
 | 
			
		||||
            expected.add_(t)
 | 
			
		||||
 | 
			
		||||
        tensors = [tensors[i].cuda(i) for i in range(nGPUs)]
 | 
			
		||||
        nccl.reduce(tensors)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(tensors[0], expected)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(nGPUs < 2, "only one GPU detected")
 | 
			
		||||
    def test_all_reduce(self):
 | 
			
		||||
        tensors = [torch.FloatTensor(128).uniform_() for i in range(nGPUs)]
 | 
			
		||||
        expected = torch.FloatTensor(128).zero_()
 | 
			
		||||
        for t in tensors:
 | 
			
		||||
            expected.add_(t)
 | 
			
		||||
 | 
			
		||||
        tensors = [tensors[i].cuda(i) for i in range(nGPUs)]
 | 
			
		||||
        nccl.all_reduce(tensors)
 | 
			
		||||
 | 
			
		||||
        for tensor in tensors:
 | 
			
		||||
            self.assertEqual(tensor, expected)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(nGPUs < 2, "only one GPU detected")
 | 
			
		||||
    def test_all_gather(self):
 | 
			
		||||
        inputs = [torch.FloatTensor(128).uniform_() for i in range(nGPUs)]
 | 
			
		||||
        expected = torch.cat(inputs, 0)
 | 
			
		||||
 | 
			
		||||
        inputs = [inputs[i].cuda(i) for i in range(nGPUs)]
 | 
			
		||||
        outputs = [torch.cuda.FloatTensor(128 * nGPUs, device=i)
 | 
			
		||||
                   for i in range(nGPUs)]
 | 
			
		||||
        nccl.all_gather(inputs, outputs)
 | 
			
		||||
 | 
			
		||||
        for tensor in outputs:
 | 
			
		||||
            self.assertEqual(tensor, expected)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(nGPUs < 2, "only one GPU detected")
 | 
			
		||||
    def test_reduce_scatter(self):
 | 
			
		||||
        in_size = 32 * nGPUs
 | 
			
		||||
        out_size = 32
 | 
			
		||||
 | 
			
		||||
        inputs = [torch.FloatTensor(in_size).uniform_() for i in range(nGPUs)]
 | 
			
		||||
        expected = torch.FloatTensor(in_size).zero_()
 | 
			
		||||
        for t in inputs:
 | 
			
		||||
            expected.add_(t)
 | 
			
		||||
        expected = expected.view(nGPUs, 32)
 | 
			
		||||
 | 
			
		||||
        inputs = [inputs[i].cuda(i) for i in range(nGPUs)]
 | 
			
		||||
        outputs = [torch.cuda.FloatTensor(out_size, device=i)
 | 
			
		||||
                   for i in range(nGPUs)]
 | 
			
		||||
        nccl.reduce_scatter(inputs, outputs)
 | 
			
		||||
 | 
			
		||||
        for i in range(nGPUs):
 | 
			
		||||
            self.assertEqual(outputs[i], expected[i])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    run_tests()
 | 
			
		||||
							
								
								
									
										3923
									
								
								test/test_nn.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3923
									
								
								test/test_nn.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										558
									
								
								test/test_optim.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										558
									
								
								test/test_optim.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,558 @@
 | 
			
		||||
import unittest
 | 
			
		||||
import functools
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
import torch
 | 
			
		||||
import torch.optim as optim
 | 
			
		||||
import torch.legacy.optim as old_optim
 | 
			
		||||
import torch.nn.functional as F
 | 
			
		||||
from torch.optim import SGD
 | 
			
		||||
from torch.autograd import Variable
 | 
			
		||||
from torch import sparse
 | 
			
		||||
from torch.optim.lr_scheduler import LambdaLR, StepLR, MultiStepLR, ExponentialLR, ReduceLROnPlateau
 | 
			
		||||
from common import TestCase, run_tests
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rosenbrock(tensor):
 | 
			
		||||
    x, y = tensor
 | 
			
		||||
    return (1 - x) ** 2 + 100 * (y - x ** 2) ** 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def drosenbrock(tensor):
 | 
			
		||||
    x, y = tensor
 | 
			
		||||
    return torch.DoubleTensor((-400 * x * (y - x ** 2) - 2 * (1 - x), 200 * (y - x ** 2)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wrap_old_fn(old_fn, **config):
 | 
			
		||||
    def wrapper(closure, params, state):
 | 
			
		||||
        return old_fn(closure, params, config, state)
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestOptim(TestCase):
 | 
			
		||||
 | 
			
		||||
    def _test_rosenbrock(self, constructor, old_fn):
 | 
			
		||||
        params_t = torch.Tensor([1.5, 1.5])
 | 
			
		||||
        state = {}
 | 
			
		||||
 | 
			
		||||
        params = Variable(torch.Tensor([1.5, 1.5]), requires_grad=True)
 | 
			
		||||
        optimizer = constructor([params])
 | 
			
		||||
 | 
			
		||||
        solution = torch.Tensor([1, 1])
 | 
			
		||||
        initial_dist = params.data.dist(solution)
 | 
			
		||||
 | 
			
		||||
        def eval():
 | 
			
		||||
            optimizer.zero_grad()
 | 
			
		||||
            loss = rosenbrock(params)
 | 
			
		||||
            loss.backward()
 | 
			
		||||
            # loss.backward() will give **slightly** different
 | 
			
		||||
            # gradients, than drosenbtock, because of a different ordering
 | 
			
		||||
            # of floating point operations. In most cases it doesn't matter,
 | 
			
		||||
            # but some optimizers are so sensitive that they can temporarily
 | 
			
		||||
            # diverge up to 1e-4, just to converge again. This makes the
 | 
			
		||||
            # comparison more stable.
 | 
			
		||||
            params.grad.data.copy_(drosenbrock(params.data))
 | 
			
		||||
            return loss
 | 
			
		||||
 | 
			
		||||
        for i in range(2000):
 | 
			
		||||
            optimizer.step(eval)
 | 
			
		||||
            old_fn(lambda _: (rosenbrock(params_t), drosenbrock(params_t)),
 | 
			
		||||
                   params_t, state)
 | 
			
		||||
            self.assertEqual(params.data, params_t)
 | 
			
		||||
 | 
			
		||||
        self.assertLessEqual(params.data.dist(solution), initial_dist)
 | 
			
		||||
 | 
			
		||||
    def _test_rosenbrock_sparse(self, constructor):
 | 
			
		||||
        params_t = torch.Tensor([1.5, 1.5])
 | 
			
		||||
 | 
			
		||||
        params = Variable(torch.Tensor([1.5, 1.5]), requires_grad=True)
 | 
			
		||||
        params_c = Variable(torch.Tensor([1.5, 1.5]), requires_grad=True)
 | 
			
		||||
        optimizer = constructor([params])
 | 
			
		||||
        optimizer_c = constructor([params_c])
 | 
			
		||||
 | 
			
		||||
        solution = torch.Tensor([1, 1])
 | 
			
		||||
        initial_dist = params.data.dist(solution)
 | 
			
		||||
 | 
			
		||||
        def eval(params, sparse_grad, w):
 | 
			
		||||
            # Depending on w, provide only the x or y gradient
 | 
			
		||||
            optimizer.zero_grad()
 | 
			
		||||
            loss = rosenbrock(params)
 | 
			
		||||
            loss.backward()
 | 
			
		||||
            grad = drosenbrock(params.data)
 | 
			
		||||
            # NB: We torture test the optimizer by returning an
 | 
			
		||||
            # uncoalesced sparse tensor
 | 
			
		||||
            if w:
 | 
			
		||||
                i = torch.LongTensor([[0, 0]])
 | 
			
		||||
                x = grad[0]
 | 
			
		||||
                v = torch.DoubleTensor([x / 4., x - x / 4.])
 | 
			
		||||
            else:
 | 
			
		||||
                i = torch.LongTensor([[1, 1]])
 | 
			
		||||
                y = grad[1]
 | 
			
		||||
                v = torch.DoubleTensor([y - y / 4., y / 4.])
 | 
			
		||||
            x = sparse.DoubleTensor(i, v, torch.Size([2]))
 | 
			
		||||
            if sparse_grad:
 | 
			
		||||
                params.grad.data = x
 | 
			
		||||
            else:
 | 
			
		||||
                params.grad.data = x.to_dense()
 | 
			
		||||
            return loss
 | 
			
		||||
 | 
			
		||||
        for i in range(2000):
 | 
			
		||||
            # Do cyclic coordinate descent
 | 
			
		||||
            w = i % 2
 | 
			
		||||
            optimizer.step(functools.partial(eval, params, True, w))
 | 
			
		||||
            optimizer_c.step(functools.partial(eval, params_c, False, w))
 | 
			
		||||
            self.assertEqual(params.data, params_c.data)
 | 
			
		||||
 | 
			
		||||
        self.assertLessEqual(params.data.dist(solution), initial_dist)
 | 
			
		||||
 | 
			
		||||
    def _test_basic_cases_template(self, weight, bias, input, constructor):
 | 
			
		||||
        weight = Variable(weight, requires_grad=True)
 | 
			
		||||
        bias = Variable(bias, requires_grad=True)
 | 
			
		||||
        input = Variable(input)
 | 
			
		||||
        optimizer = constructor(weight, bias)
 | 
			
		||||
 | 
			
		||||
        def fn():
 | 
			
		||||
            optimizer.zero_grad()
 | 
			
		||||
            y = weight.mv(input)
 | 
			
		||||
            if y.is_cuda and bias.is_cuda and y.get_device() != bias.get_device():
 | 
			
		||||
                y = y.cuda(bias.get_device())
 | 
			
		||||
            loss = (y + bias).pow(2).sum()
 | 
			
		||||
            loss.backward()
 | 
			
		||||
            return loss
 | 
			
		||||
 | 
			
		||||
        initial_value = fn().data[0]
 | 
			
		||||
        for i in range(200):
 | 
			
		||||
            optimizer.step(fn)
 | 
			
		||||
        self.assertLess(fn().data[0], initial_value)
 | 
			
		||||
 | 
			
		||||
    def _test_state_dict(self, weight, bias, input, constructor):
 | 
			
		||||
        weight = Variable(weight, requires_grad=True)
 | 
			
		||||
        bias = Variable(bias, requires_grad=True)
 | 
			
		||||
        input = Variable(input)
 | 
			
		||||
 | 
			
		||||
        def fn_base(optimizer, weight, bias):
 | 
			
		||||
            optimizer.zero_grad()
 | 
			
		||||
            loss = (weight.mv(input) + bias).pow(2).sum()
 | 
			
		||||
            loss.backward()
 | 
			
		||||
            return loss
 | 
			
		||||
 | 
			
		||||
        optimizer = constructor(weight, bias)
 | 
			
		||||
        fn = functools.partial(fn_base, optimizer, weight, bias)
 | 
			
		||||
 | 
			
		||||
        # Prime the optimizer
 | 
			
		||||
        for i in range(20):
 | 
			
		||||
            optimizer.step(fn)
 | 
			
		||||
        # Clone the weights and construct new optimizer for them
 | 
			
		||||
        weight_c = Variable(weight.data.clone(), requires_grad=True)
 | 
			
		||||
        bias_c = Variable(bias.data.clone(), requires_grad=True)
 | 
			
		||||
        optimizer_c = constructor(weight_c, bias_c)
 | 
			
		||||
        fn_c = functools.partial(fn_base, optimizer_c, weight_c, bias_c)
 | 
			
		||||
        # Load state dict
 | 
			
		||||
        state_dict = deepcopy(optimizer.state_dict())
 | 
			
		||||
        state_dict_c = deepcopy(optimizer.state_dict())
 | 
			
		||||
        optimizer_c.load_state_dict(state_dict_c)
 | 
			
		||||
        # Run both optimizations in parallel
 | 
			
		||||
        for i in range(20):
 | 
			
		||||
            optimizer.step(fn)
 | 
			
		||||
            optimizer_c.step(fn_c)
 | 
			
		||||
            self.assertEqual(weight, weight_c)
 | 
			
		||||
            self.assertEqual(bias, bias_c)
 | 
			
		||||
        # Make sure state dict wasn't modified
 | 
			
		||||
        self.assertEqual(state_dict, state_dict_c)
 | 
			
		||||
 | 
			
		||||
    def _test_basic_cases(self, constructor, ignore_multidevice=False):
 | 
			
		||||
        self._test_state_dict(
 | 
			
		||||
            torch.randn(10, 5),
 | 
			
		||||
            torch.randn(10),
 | 
			
		||||
            torch.randn(5),
 | 
			
		||||
            constructor
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases_template(
 | 
			
		||||
            torch.randn(10, 5),
 | 
			
		||||
            torch.randn(10),
 | 
			
		||||
            torch.randn(5),
 | 
			
		||||
            constructor
 | 
			
		||||
        )
 | 
			
		||||
        # non-contiguous parameters
 | 
			
		||||
        self._test_basic_cases_template(
 | 
			
		||||
            torch.randn(10, 5, 2)[..., 0],
 | 
			
		||||
            torch.randn(10, 2)[..., 0],
 | 
			
		||||
            torch.randn(5),
 | 
			
		||||
            constructor
 | 
			
		||||
        )
 | 
			
		||||
        # CUDA
 | 
			
		||||
        if not torch.cuda.is_available():
 | 
			
		||||
            return
 | 
			
		||||
        self._test_basic_cases_template(
 | 
			
		||||
            torch.randn(10, 5).cuda(),
 | 
			
		||||
            torch.randn(10).cuda(),
 | 
			
		||||
            torch.randn(5).cuda(),
 | 
			
		||||
            constructor
 | 
			
		||||
        )
 | 
			
		||||
        # Multi-GPU
 | 
			
		||||
        if not torch.cuda.device_count() > 1 or ignore_multidevice:
 | 
			
		||||
            return
 | 
			
		||||
        self._test_basic_cases_template(
 | 
			
		||||
            torch.randn(10, 5).cuda(0),
 | 
			
		||||
            torch.randn(10).cuda(1),
 | 
			
		||||
            torch.randn(5).cuda(0),
 | 
			
		||||
            constructor
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _build_params_dict(self, weight, bias, **kwargs):
 | 
			
		||||
        return [dict(params=[weight]), dict(params=[bias], **kwargs)]
 | 
			
		||||
 | 
			
		||||
    def _build_params_dict_single(self, weight, bias, **kwargs):
 | 
			
		||||
        return [dict(params=bias, **kwargs)]
 | 
			
		||||
 | 
			
		||||
    def test_sgd(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.SGD(params, lr=1e-3),
 | 
			
		||||
            wrap_old_fn(old_optim.sgd, learningRate=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.SGD(params, lr=1e-3, momentum=0.9,
 | 
			
		||||
                                     dampening=0, weight_decay=1e-4),
 | 
			
		||||
            wrap_old_fn(old_optim.sgd, learningRate=1e-3, momentum=0.9,
 | 
			
		||||
                        dampening=0, weightDecay=1e-4)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.SGD([weight, bias], lr=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.SGD(
 | 
			
		||||
                self._build_params_dict(weight, bias, lr=1e-2),
 | 
			
		||||
                lr=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.SGD(
 | 
			
		||||
                self._build_params_dict_single(weight, bias, lr=1e-2),
 | 
			
		||||
                lr=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_adam(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adam(params, lr=1e-2),
 | 
			
		||||
            wrap_old_fn(old_optim.adam, learningRate=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adam(params, lr=1e-2, weight_decay=1e-2),
 | 
			
		||||
            wrap_old_fn(old_optim.adam, learningRate=1e-2, weightDecay=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adam([weight, bias], lr=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adam(
 | 
			
		||||
                self._build_params_dict(weight, bias, lr=1e-2),
 | 
			
		||||
                lr=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_adadelta(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adadelta(params),
 | 
			
		||||
            wrap_old_fn(old_optim.adadelta)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adadelta(params, rho=0.95),
 | 
			
		||||
            wrap_old_fn(old_optim.adadelta, rho=0.95)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adadelta(params, weight_decay=1e-2),
 | 
			
		||||
            wrap_old_fn(old_optim.adadelta, weightDecay=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adadelta([weight, bias])
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adadelta(
 | 
			
		||||
                self._build_params_dict(weight, bias, rho=0.95))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_adagrad(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adagrad(params, lr=1e-1),
 | 
			
		||||
            wrap_old_fn(old_optim.adagrad, learningRate=1e-1)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adagrad(params, lr=1e-1, lr_decay=1e-3),
 | 
			
		||||
            wrap_old_fn(old_optim.adagrad, learningRate=1e-1, learningRateDecay=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adagrad(params, lr=1e-1, weight_decay=1e-2),
 | 
			
		||||
            wrap_old_fn(old_optim.adagrad, learningRate=1e-1, weightDecay=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adagrad([weight, bias], lr=1e-1)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adagrad(
 | 
			
		||||
                self._build_params_dict(weight, bias, lr=1e-2),
 | 
			
		||||
                lr=1e-1)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_adagrad_sparse(self):
 | 
			
		||||
        self._test_rosenbrock_sparse(
 | 
			
		||||
            lambda params: optim.Adagrad(params, lr=1e-1)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_adamax(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adamax(params, lr=1e-1),
 | 
			
		||||
            wrap_old_fn(old_optim.adamax, learningRate=1e-1)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adamax(params, lr=1e-1, weight_decay=1e-2),
 | 
			
		||||
            wrap_old_fn(old_optim.adamax, learningRate=1e-1, weightDecay=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Adamax(params, lr=1e-1, betas=(0.95, 0.998)),
 | 
			
		||||
            wrap_old_fn(old_optim.adamax, learningRate=1e-1, beta1=0.95, beta2=0.998)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adagrad([weight, bias], lr=1e-1)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adagrad(
 | 
			
		||||
                self._build_params_dict(weight, bias, lr=1e-2),
 | 
			
		||||
                lr=1e-1)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_rmsprop(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.RMSprop(params, lr=1e-2),
 | 
			
		||||
            wrap_old_fn(old_optim.rmsprop, learningRate=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.RMSprop(params, lr=1e-2, weight_decay=1e-2),
 | 
			
		||||
            wrap_old_fn(old_optim.rmsprop, learningRate=1e-2, weightDecay=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.RMSprop(params, lr=1e-2, alpha=0.95),
 | 
			
		||||
            wrap_old_fn(old_optim.rmsprop, learningRate=1e-2, alpha=0.95)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adagrad([weight, bias], lr=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Adagrad(
 | 
			
		||||
                self._build_params_dict(weight, bias, lr=1e-3),
 | 
			
		||||
                lr=1e-2)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_asgd(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.ASGD(params, lr=1e-3),
 | 
			
		||||
            wrap_old_fn(old_optim.asgd, eta0=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.ASGD(params, lr=1e-3, alpha=0.8),
 | 
			
		||||
            wrap_old_fn(old_optim.asgd, eta0=1e-3, alpha=0.8)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.ASGD(params, lr=1e-3, t0=1e3),
 | 
			
		||||
            wrap_old_fn(old_optim.asgd, eta0=1e-3, t0=1e3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.ASGD([weight, bias], lr=1e-3, t0=100)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.ASGD(
 | 
			
		||||
                self._build_params_dict(weight, bias, lr=1e-2),
 | 
			
		||||
                lr=1e-3, t0=100)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_rprop(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Rprop(params, lr=1e-3),
 | 
			
		||||
            wrap_old_fn(old_optim.rprop, stepsize=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Rprop(params, lr=1e-3, etas=(0.6, 1.1)),
 | 
			
		||||
            wrap_old_fn(old_optim.rprop, stepsize=1e-3, etaminus=0.6, etaplus=1.1)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.Rprop(params, lr=1e-3, step_sizes=(1e-4, 3)),
 | 
			
		||||
            wrap_old_fn(old_optim.rprop, stepsize=1e-3, stepsizemin=1e-4, stepsizemax=3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Rprop([weight, bias], lr=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.Rprop(
 | 
			
		||||
                self._build_params_dict(weight, bias, lr=1e-2),
 | 
			
		||||
                lr=1e-3)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_lbfgs(self):
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.LBFGS(params),
 | 
			
		||||
            wrap_old_fn(old_optim.lbfgs)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_rosenbrock(
 | 
			
		||||
            lambda params: optim.LBFGS(params, lr=5e-2, max_iter=5),
 | 
			
		||||
            wrap_old_fn(old_optim.lbfgs, learningRate=5e-2, maxIter=5)
 | 
			
		||||
        )
 | 
			
		||||
        self._test_basic_cases(
 | 
			
		||||
            lambda weight, bias: optim.LBFGS([weight, bias]),
 | 
			
		||||
            ignore_multidevice=True
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_invalid_param_type(self):
 | 
			
		||||
        with self.assertRaises(TypeError):
 | 
			
		||||
            optim.SGD(Variable(torch.randn(5, 5)), lr=3)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SchedulerTestNet(torch.nn.Module):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(SchedulerTestNet, self).__init__()
 | 
			
		||||
        self.conv1 = torch.nn.Conv2d(1, 1, 1)
 | 
			
		||||
        self.conv2 = torch.nn.Conv2d(1, 1, 1)
 | 
			
		||||
 | 
			
		||||
    def forward(self, x):
 | 
			
		||||
        return self.conv2(F.relu(self.conv1(x)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestLRScheduler(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.net = SchedulerTestNet()
 | 
			
		||||
        self.opt = SGD(
 | 
			
		||||
            [{'params': self.net.conv1.parameters()}, {'params': self.net.conv2.parameters(), 'lr': 0.5}],
 | 
			
		||||
            lr=0.05)
 | 
			
		||||
 | 
			
		||||
    def test_step_lr(self):
 | 
			
		||||
        # lr = 0.05     if epoch < 3
 | 
			
		||||
        # lr = 0.005    if 30 <= epoch < 6
 | 
			
		||||
        # lr = 0.0005   if epoch >= 9
 | 
			
		||||
        single_targets = [0.05] * 3 + [0.005] * 3 + [0.0005] * 3 + [0.00005] * 3
 | 
			
		||||
        targets = [single_targets, list(map(lambda x: x * 10, single_targets))]
 | 
			
		||||
        scheduler = StepLR(self.opt, gamma=0.1, step_size=3)
 | 
			
		||||
        epochs = 10
 | 
			
		||||
        self._test(scheduler, targets, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_multi_step_lr(self):
 | 
			
		||||
        # lr = 0.05     if epoch < 2
 | 
			
		||||
        # lr = 0.005    if 2 <= epoch < 5
 | 
			
		||||
        # lr = 0.0005   if epoch < 9
 | 
			
		||||
        # lr = 0.00005   if epoch >= 9
 | 
			
		||||
        single_targets = [0.05] * 2 + [0.005] * 3 + [0.0005] * 4 + [0.00005] * 3
 | 
			
		||||
        targets = [single_targets, list(map(lambda x: x * 10, single_targets))]
 | 
			
		||||
        scheduler = MultiStepLR(self.opt, gamma=0.1, milestones=[2, 5, 9])
 | 
			
		||||
        epochs = 10
 | 
			
		||||
        self._test(scheduler, targets, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_exp_lr(self):
 | 
			
		||||
        single_targets = [0.05 * (0.9 ** x) for x in range(10)]
 | 
			
		||||
        targets = [single_targets, list(map(lambda x: x * 10, single_targets))]
 | 
			
		||||
        scheduler = ExponentialLR(self.opt, gamma=0.9)
 | 
			
		||||
        epochs = 10
 | 
			
		||||
        self._test(scheduler, targets, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau1(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * 20]
 | 
			
		||||
        metrics = [10 - i * 0.0167 for i in range(20)]
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, threshold_mode='abs', mode='min',
 | 
			
		||||
                                      threshold=0.01, patience=5, cooldown=5)
 | 
			
		||||
        epochs = 10
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau2(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * 6 + [0.05] * 7 + [0.005] * 7 + [0.0005] * 2]
 | 
			
		||||
        metrics = [10 - i * 0.0165 for i in range(22)]
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, patience=5, cooldown=0, threshold_mode='abs',
 | 
			
		||||
                                      mode='min', threshold=0.1)
 | 
			
		||||
        epochs = 22
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau3(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * (2 + 6) + [0.05] * (5 + 6) + [0.005] * 4]
 | 
			
		||||
        metrics = [-0.8] * 2 + [-0.234] * 20
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, mode='max', patience=5, cooldown=5,
 | 
			
		||||
                                      threshold_mode='abs')
 | 
			
		||||
        epochs = 22
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau4(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * 20]
 | 
			
		||||
        metrics = [1.5 * (1.025 ** i) for i in range(20)]  # 1.025 > 1.1**0.25
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, mode='max', patience=3,
 | 
			
		||||
                                      threshold_mode='rel', threshold=0.1)
 | 
			
		||||
        epochs = 20
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau5(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * 6 + [0.05] * (5 + 6) + [0.005] * 4]
 | 
			
		||||
        metrics = [1.5 * (1.005 ** i) for i in range(20)]
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, mode='max', threshold_mode='rel',
 | 
			
		||||
                                      threshold=0.1, patience=5, cooldown=5)
 | 
			
		||||
        epochs = 20
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau6(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * 20]
 | 
			
		||||
        metrics = [1.5 * (0.85 ** i) for i in range(20)]
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, mode='min', threshold_mode='rel',
 | 
			
		||||
                                      threshold=0.1)
 | 
			
		||||
        epochs = 20
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau7(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * 6 + [0.05] * (5 + 6) + [0.005] * 4]
 | 
			
		||||
        metrics = [1] * 7 + [0.6] + [0.5] * 12
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, mode='min', threshold_mode='rel',
 | 
			
		||||
                                      threshold=0.1, patience=5, cooldown=5)
 | 
			
		||||
        epochs = 20
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_reduce_lr_on_plateau8(self):
 | 
			
		||||
        for param_group in self.opt.param_groups:
 | 
			
		||||
            param_group['lr'] = 0.5
 | 
			
		||||
        targets = [[0.5] * 6 + [0.4] * 14, [0.5] * 6 + [0.3] * 14]
 | 
			
		||||
        metrics = [1.5 * (1.005 ** i) for i in range(20)]
 | 
			
		||||
        scheduler = ReduceLROnPlateau(self.opt, mode='max', threshold_mode='rel', min_lr=[0.4, 0.3],
 | 
			
		||||
                                      threshold=0.1, patience=5, cooldown=5)
 | 
			
		||||
        epochs = 20
 | 
			
		||||
        self._test_reduce_lr_on_plateau(scheduler, targets, metrics, epochs)
 | 
			
		||||
 | 
			
		||||
    def test_lambda_lr(self):
 | 
			
		||||
        self.opt.param_groups[0]['lr'] = 0.05
 | 
			
		||||
        self.opt.param_groups[1]['lr'] = 0.4
 | 
			
		||||
        targets = [[0.05 * (0.9 ** x) for x in range(10)], [0.4 * (0.8 ** x) for x in range(10)]]
 | 
			
		||||
        scheduler = LambdaLR(self.opt,
 | 
			
		||||
                             lr_lambda=[lambda x1: 0.9 ** x1, lambda x2: 0.8 ** x2])
 | 
			
		||||
        epochs = 10
 | 
			
		||||
        self._test(scheduler, targets, epochs)
 | 
			
		||||
 | 
			
		||||
    def _test(self, scheduler, targets, epochs=10):
 | 
			
		||||
        for epoch in range(epochs):
 | 
			
		||||
            scheduler.step(epoch)
 | 
			
		||||
            for param_group, target in zip(self.opt.param_groups, targets):
 | 
			
		||||
                self.assertAlmostEqual(target[epoch], param_group['lr'],
 | 
			
		||||
                                       msg='LR is wrong in epoch {}: expected {}, got {}'.format(
 | 
			
		||||
                                           epoch, target[epoch], param_group['lr']), delta=1e-5)
 | 
			
		||||
 | 
			
		||||
    def _test_reduce_lr_on_plateau(self, scheduler, targets, metrics, epochs=10, verbose=False):
 | 
			
		||||
        for epoch in range(epochs):
 | 
			
		||||
            scheduler.step(metrics[epoch])
 | 
			
		||||
            if verbose:
 | 
			
		||||
                print('epoch{}:\tlr={}'.format(epoch, self.opt.param_groups[0]['lr']))
 | 
			
		||||
            for param_group, target in zip(self.opt.param_groups, targets):
 | 
			
		||||
                self.assertAlmostEqual(target[epoch], param_group['lr'],
 | 
			
		||||
                                       msg='LR is wrong in epoch {}: expected {}, got {}'.format(
 | 
			
		||||
                                           epoch, target[epoch], param_group['lr']), delta=1e-5)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    run_tests()
 | 
			
		||||
							
								
								
									
										625
									
								
								test/test_sparse.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										625
									
								
								test/test_sparse.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,625 @@
 | 
			
		||||
import torch
 | 
			
		||||
from torch import sparse
 | 
			
		||||
 | 
			
		||||
import itertools
 | 
			
		||||
import random
 | 
			
		||||
import unittest
 | 
			
		||||
from common import TestCase, run_tests
 | 
			
		||||
from common_nn import TEST_CUDA
 | 
			
		||||
from numbers import Number
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cpu_only(inner):
 | 
			
		||||
    def outer(self, *args, **kwargs):
 | 
			
		||||
        if self.is_cuda:
 | 
			
		||||
            raise unittest.SkipTest("Test is CPU-only")
 | 
			
		||||
        inner(self, *args, **kwargs)
 | 
			
		||||
    return outer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cuda_only(inner):
 | 
			
		||||
    def outer(self, *args, **kwargs):
 | 
			
		||||
        if not self.is_cuda:
 | 
			
		||||
            raise unittest.SkipTest("Test is GPU-only")
 | 
			
		||||
        inner(self, *args, **kwargs)
 | 
			
		||||
    return outer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestSparse(TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        # These parameters control the various ways we can run the test.
 | 
			
		||||
        # We will subclass and override this method to implement CUDA
 | 
			
		||||
        # tests
 | 
			
		||||
        self.is_cuda = False
 | 
			
		||||
        self.is_uncoalesced = False
 | 
			
		||||
        self.IndexTensor = torch.LongTensor
 | 
			
		||||
        self.ValueTensor = torch.DoubleTensor
 | 
			
		||||
        self.SparseTensor = torch.sparse.DoubleTensor
 | 
			
		||||
 | 
			
		||||
    def _gen_sparse(self, d, nnz, with_size):
 | 
			
		||||
        # TODO: Consider implementing this in the CUDA case by directly
 | 
			
		||||
        # performing the operations on the GPU.  You won't be able to
 | 
			
		||||
        # use torch.rand/torch.randn in this case because they are
 | 
			
		||||
        # CPU-only.  If you do this, you can remove the is_cuda branch
 | 
			
		||||
        # at the end.
 | 
			
		||||
        #
 | 
			
		||||
        # If you do this, be sure to update assert_uncoalesced too
 | 
			
		||||
 | 
			
		||||
        if isinstance(with_size, Number):
 | 
			
		||||
            with_size = [with_size] * d
 | 
			
		||||
 | 
			
		||||
        if self.is_uncoalesced:
 | 
			
		||||
            # We want to generate a tensor with a lot of uncoalesced
 | 
			
		||||
            # entries to stress test whether or not we handle this
 | 
			
		||||
            # (subtle) case correctly
 | 
			
		||||
            v_size = [nnz * 2] + list(with_size[d:])
 | 
			
		||||
            v = torch.randn(*v_size)
 | 
			
		||||
            r = torch.rand(d, nnz)
 | 
			
		||||
            # Repeat the indexes, so every position shows up twice
 | 
			
		||||
            i = torch.cat([r, r], dim=1) * \
 | 
			
		||||
                torch.Tensor(with_size[:d]).repeat(nnz * 2, 1).transpose(0, 1)
 | 
			
		||||
            i = i.type(torch.LongTensor)
 | 
			
		||||
            x = torch.sparse.DoubleTensor(i, v, torch.Size(with_size))
 | 
			
		||||
            self.assert_uncoalesced(x)
 | 
			
		||||
        else:
 | 
			
		||||
            # Generate a sparse tensor with d sparse dimensions; the
 | 
			
		||||
            # rest the dimensions with_size[d:] are dense.
 | 
			
		||||
            v_size = [nnz] + list(with_size[d:])
 | 
			
		||||
            v = torch.randn(*v_size)
 | 
			
		||||
            i = torch.rand(d, nnz) * \
 | 
			
		||||
                torch.Tensor(with_size[:d]).repeat(nnz, 1).transpose(0, 1)
 | 
			
		||||
            i = i.type(torch.LongTensor)
 | 
			
		||||
            x = torch.sparse.DoubleTensor(i, v, torch.Size(with_size))
 | 
			
		||||
 | 
			
		||||
        if self.is_cuda:
 | 
			
		||||
            return x.cuda(), i.cuda(), v.cuda()
 | 
			
		||||
        else:
 | 
			
		||||
            return x, i.clone(), v.clone()
 | 
			
		||||
 | 
			
		||||
    def assert_uncoalesced(self, x):
 | 
			
		||||
        """
 | 
			
		||||
        Test if a CPU tensor is uncoalesced.  This is used to ensure
 | 
			
		||||
        correctness of the uncoalesced tensor generation algorithm.
 | 
			
		||||
        """
 | 
			
		||||
        assert not x.is_coalesced()
 | 
			
		||||
        # Strategy: construct a new sparse tensor with the raw value
 | 
			
		||||
        # field overwritten to a tensor of ones, coalesce it, and then
 | 
			
		||||
        # check if any value entries are > 1 (which indicates that the
 | 
			
		||||
        # original was uncoalesced.)
 | 
			
		||||
        i = x._indices().clone()
 | 
			
		||||
        v = x._values().clone().fill_(1)
 | 
			
		||||
        y = torch.sparse.DoubleTensor(i, v, x.size())
 | 
			
		||||
        z = self.safeCoalesce(y)
 | 
			
		||||
        assert (z._values() > 1).sum() > 0
 | 
			
		||||
 | 
			
		||||
    def randn(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Variant of torch.randn that also works in the TEST_CUDA case.
 | 
			
		||||
        """
 | 
			
		||||
        # TODO: Put this in torch.cuda.randn
 | 
			
		||||
        return self.ValueTensor(*args, **kwargs).normal_()
 | 
			
		||||
 | 
			
		||||
    def test_basic(self):
 | 
			
		||||
        x, i, v = self._gen_sparse(3, 10, 100)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(i, x._indices())
 | 
			
		||||
        self.assertEqual(v, x._values())
 | 
			
		||||
 | 
			
		||||
        x, i, v = self._gen_sparse(3, 10, [100, 100, 100])
 | 
			
		||||
        self.assertEqual(i, x._indices())
 | 
			
		||||
        self.assertEqual(v, x._values())
 | 
			
		||||
        self.assertEqual(x.ndimension(), 3)
 | 
			
		||||
        self.assertEqual(x.coalesce()._nnz(), 10)
 | 
			
		||||
        for i in range(3):
 | 
			
		||||
            self.assertEqual(x.size(i), 100)
 | 
			
		||||
 | 
			
		||||
        # Make sure we can access empty indices / values
 | 
			
		||||
        x = self.SparseTensor()
 | 
			
		||||
        self.assertEqual(x._indices().numel(), 0)
 | 
			
		||||
        self.assertEqual(x._values().numel(), 0)
 | 
			
		||||
 | 
			
		||||
    def test_to_dense(self):
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [0, 1, 2, 2],
 | 
			
		||||
            [0, 0, 0, 3],
 | 
			
		||||
            [0, 0, 1, 4],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([2, 1, 3, 4])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3, 4, 5]))
 | 
			
		||||
        res = self.ValueTensor([
 | 
			
		||||
            [[2, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0]],
 | 
			
		||||
            [[1, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0]],
 | 
			
		||||
            [[0, 3, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 0],
 | 
			
		||||
             [0, 0, 0, 0, 4]],
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        x.to_dense()  # Tests double to_dense for memory corruption
 | 
			
		||||
        x.to_dense()
 | 
			
		||||
        x.to_dense()
 | 
			
		||||
        self.assertEqual(res, x.to_dense())
 | 
			
		||||
 | 
			
		||||
    def test_shared(self):
 | 
			
		||||
        i = self.IndexTensor([[2]])
 | 
			
		||||
        v = self.ValueTensor([5])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3]))
 | 
			
		||||
        v[0] = 6
 | 
			
		||||
        self.assertEqual(self.ValueTensor([0, 0, 6]), x.to_dense())
 | 
			
		||||
        i[0][0] = 0
 | 
			
		||||
        self.assertEqual(self.ValueTensor([6, 0, 0]), x.to_dense())
 | 
			
		||||
 | 
			
		||||
    def test_to_dense_hybrid(self):
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [0, 1, 2, 2],
 | 
			
		||||
            [0, 0, 0, 3],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([[2, 3], [1, 2], [3, 4], [4, 5]])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3, 4, 2]))
 | 
			
		||||
        res = self.ValueTensor([
 | 
			
		||||
            [[2, 3],
 | 
			
		||||
             [0, 0],
 | 
			
		||||
             [0, 0],
 | 
			
		||||
             [0, 0]],
 | 
			
		||||
            [[1, 2],
 | 
			
		||||
             [0, 0],
 | 
			
		||||
             [0, 0],
 | 
			
		||||
             [0, 0]],
 | 
			
		||||
            [[3, 4],
 | 
			
		||||
             [0, 0],
 | 
			
		||||
             [0, 0],
 | 
			
		||||
             [4, 5]],
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        x.to_dense()  # Tests double to_dense for memory corruption
 | 
			
		||||
        x.to_dense()
 | 
			
		||||
        x.to_dense()
 | 
			
		||||
        self.assertEqual(res, x.to_dense())
 | 
			
		||||
 | 
			
		||||
    def test_contig(self):
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [1, 0, 35, 14, 39, 6, 71, 66, 40, 27],
 | 
			
		||||
            [92, 31, 62, 50, 22, 65, 89, 74, 56, 34],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([100, 100]))
 | 
			
		||||
        exp_i = self.IndexTensor([
 | 
			
		||||
            [0, 1, 6, 14, 27, 35, 39, 40, 66, 71],
 | 
			
		||||
            [31, 92, 65, 50, 34, 62, 22, 56, 74, 89],
 | 
			
		||||
        ])
 | 
			
		||||
        exp_v = self.ValueTensor([2, 1, 6, 4, 10, 3, 5, 9, 8, 7])
 | 
			
		||||
        x = self.safeCoalesce(x)
 | 
			
		||||
        self.assertEqual(exp_i, x._indices())
 | 
			
		||||
        self.assertEqual(exp_v, x._values())
 | 
			
		||||
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [2, 0, 2, 1],
 | 
			
		||||
            [0, 0, 3, 0],
 | 
			
		||||
            [1, 0, 4, 0],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([3, 2, 4, 1])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3, 4, 5]))
 | 
			
		||||
        exp_i = self.IndexTensor([
 | 
			
		||||
            [0, 1, 2, 2],
 | 
			
		||||
            [0, 0, 0, 3],
 | 
			
		||||
            [0, 0, 1, 4],
 | 
			
		||||
        ])
 | 
			
		||||
        exp_v = self.ValueTensor([2, 1, 3, 4])
 | 
			
		||||
 | 
			
		||||
        x = self.safeCoalesce(x)
 | 
			
		||||
        self.assertEqual(exp_i, x._indices())
 | 
			
		||||
        self.assertEqual(exp_v, x._values())
 | 
			
		||||
 | 
			
		||||
        # Duplicate indices
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [0, 0, 2, 0],
 | 
			
		||||
            [0, 0, 3, 0],
 | 
			
		||||
            [0, 0, 4, 0],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([3, 2, 4, 1])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3, 4, 5]))
 | 
			
		||||
        exp_i = self.IndexTensor([
 | 
			
		||||
            [0, 2],
 | 
			
		||||
            [0, 3],
 | 
			
		||||
            [0, 4],
 | 
			
		||||
        ])
 | 
			
		||||
        exp_v = self.ValueTensor([6, 4])
 | 
			
		||||
 | 
			
		||||
        x = self.safeCoalesce(x)
 | 
			
		||||
        self.assertEqual(exp_i, x._indices())
 | 
			
		||||
        self.assertEqual(exp_v, x._values())
 | 
			
		||||
 | 
			
		||||
    def test_contig_hybrid(self):
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [1, 0, 35, 14, 39, 6, 71, 66, 40, 27],
 | 
			
		||||
            [92, 31, 62, 50, 22, 65, 89, 74, 56, 34],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([
 | 
			
		||||
            [1, 2], [2, 3], [3, 4], [4, 5], [5, 6],
 | 
			
		||||
            [6, 7], [7, 8], [8, 9], [9, 10], [10, 11],
 | 
			
		||||
        ])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([100, 100, 2]))
 | 
			
		||||
        exp_i = self.IndexTensor([
 | 
			
		||||
            [0, 1, 6, 14, 27, 35, 39, 40, 66, 71],
 | 
			
		||||
            [31, 92, 65, 50, 34, 62, 22, 56, 74, 89],
 | 
			
		||||
        ])
 | 
			
		||||
        exp_v = self.ValueTensor([
 | 
			
		||||
            [2, 3], [1, 2], [6, 7], [4, 5], [10, 11],
 | 
			
		||||
            [3, 4], [5, 6], [9, 10], [8, 9], [7, 8],
 | 
			
		||||
        ])
 | 
			
		||||
        x = self.safeCoalesce(x)
 | 
			
		||||
        self.assertEqual(exp_i, x._indices())
 | 
			
		||||
        self.assertEqual(exp_v, x._values())
 | 
			
		||||
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [2, 0, 2, 1],
 | 
			
		||||
            [0, 0, 3, 0],
 | 
			
		||||
            [1, 0, 4, 0],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([[3, 3, 3], [2, 2, 2], [4, 4, 4], [1, 1, 1]])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3, 4, 5, 3]))
 | 
			
		||||
        exp_i = self.IndexTensor([
 | 
			
		||||
            [0, 1, 2, 2],
 | 
			
		||||
            [0, 0, 0, 3],
 | 
			
		||||
            [0, 0, 1, 4],
 | 
			
		||||
        ])
 | 
			
		||||
        exp_v = self.ValueTensor([[2, 2, 2], [1, 1, 1], [3, 3, 3], [4, 4, 4]])
 | 
			
		||||
 | 
			
		||||
        x = self.safeCoalesce(x)
 | 
			
		||||
        self.assertEqual(exp_i, x._indices())
 | 
			
		||||
        self.assertEqual(exp_v, x._values())
 | 
			
		||||
 | 
			
		||||
        # Duplicate indices
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [0, 0, 2, 0],
 | 
			
		||||
            [0, 0, 3, 0],
 | 
			
		||||
            [0, 0, 4, 0],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([[3, 2, 3], [2, 1, 1], [4, 3, 4], [1, 1, 1]])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3, 4, 5, 3]))
 | 
			
		||||
        exp_i = self.IndexTensor([
 | 
			
		||||
            [0, 2],
 | 
			
		||||
            [0, 3],
 | 
			
		||||
            [0, 4],
 | 
			
		||||
        ])
 | 
			
		||||
        exp_v = self.ValueTensor([[6, 4, 5], [4, 3, 4]])
 | 
			
		||||
 | 
			
		||||
        x = self.safeCoalesce(x)
 | 
			
		||||
        self.assertEqual(exp_i, x._indices())
 | 
			
		||||
        self.assertEqual(exp_v, x._values())
 | 
			
		||||
 | 
			
		||||
    def test_clone(self):
 | 
			
		||||
        x, _, _ = self._gen_sparse(4, 20, 5)
 | 
			
		||||
        if self.is_uncoalesced:
 | 
			
		||||
            self.assertFalse(x.is_coalesced())
 | 
			
		||||
            y = x.clone()
 | 
			
		||||
            self.assertFalse(y.is_coalesced())
 | 
			
		||||
        x = x.coalesce()
 | 
			
		||||
        self.assertTrue(x.is_coalesced())
 | 
			
		||||
        y = x.clone()
 | 
			
		||||
        self.assertTrue(y.is_coalesced())
 | 
			
		||||
 | 
			
		||||
    def test_transpose(self):
 | 
			
		||||
        x = self._gen_sparse(4, 20, 5)[0]
 | 
			
		||||
        y = x.to_dense()
 | 
			
		||||
 | 
			
		||||
        for i, j in itertools.combinations(range(4), 2):
 | 
			
		||||
            x = x.transpose_(i, j)
 | 
			
		||||
            y = y.transpose(i, j)
 | 
			
		||||
            self.assertEqual(x.to_dense(), y)
 | 
			
		||||
 | 
			
		||||
            x = x.transpose(i, j)
 | 
			
		||||
            y = y.transpose(i, j)
 | 
			
		||||
            self.assertEqual(x.to_dense(), y)
 | 
			
		||||
 | 
			
		||||
    @cpu_only
 | 
			
		||||
    def test_mm(self):
 | 
			
		||||
        def test_shape(di, dj, dk):
 | 
			
		||||
            x, _, _ = self._gen_sparse(2, 20, [di, dj])
 | 
			
		||||
            t = torch.randn(di, dk)
 | 
			
		||||
            y = torch.randn(dj, dk)
 | 
			
		||||
            alpha = random.random()
 | 
			
		||||
            beta = random.random()
 | 
			
		||||
 | 
			
		||||
            res = torch.addmm(alpha, t, beta, x, y)
 | 
			
		||||
            expected = torch.addmm(alpha, t, beta, x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
            res = torch.addmm(t, x, y)
 | 
			
		||||
            expected = torch.addmm(t, x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
            res = torch.mm(x, y)
 | 
			
		||||
            expected = torch.mm(x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
        test_shape(10, 100, 100)
 | 
			
		||||
        test_shape(100, 1000, 200)
 | 
			
		||||
        test_shape(64, 10000, 300)
 | 
			
		||||
 | 
			
		||||
    @cpu_only
 | 
			
		||||
    def test_saddmm(self):
 | 
			
		||||
        def test_shape(di, dj, dk):
 | 
			
		||||
            x = self._gen_sparse(2, 20, [di, dj])[0]
 | 
			
		||||
            t = self._gen_sparse(2, 20, [di, dk])[0]
 | 
			
		||||
            y = torch.randn(dj, dk)
 | 
			
		||||
            alpha = random.random()
 | 
			
		||||
            beta = random.random()
 | 
			
		||||
 | 
			
		||||
            res = torch.saddmm(alpha, t, beta, x, y)
 | 
			
		||||
            expected = torch.addmm(alpha, t.to_dense(), beta, x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
            res = torch.saddmm(t, x, y)
 | 
			
		||||
            expected = torch.addmm(t.to_dense(), x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
            res = torch.smm(x, y)
 | 
			
		||||
            expected = torch.mm(x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        test_shape(7, 5, 3)
 | 
			
		||||
        test_shape(1000, 100, 100)
 | 
			
		||||
        test_shape(3000, 64, 300)
 | 
			
		||||
 | 
			
		||||
    def test_dsmm(self):
 | 
			
		||||
        def test_shape(di, dj, dk):
 | 
			
		||||
            x = self._gen_sparse(2, 20, [di, dj])[0]
 | 
			
		||||
            y = self.randn(dj, dk)
 | 
			
		||||
 | 
			
		||||
            res = torch.dsmm(x, y)
 | 
			
		||||
            expected = torch.mm(x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
        test_shape(7, 5, 3)
 | 
			
		||||
        test_shape(1000, 100, 100)
 | 
			
		||||
        test_shape(3000, 64, 300)
 | 
			
		||||
 | 
			
		||||
    def test_hsmm(self):
 | 
			
		||||
        def test_shape(di, dj, dk):
 | 
			
		||||
            x = self._gen_sparse(2, 20, [di, dj])[0]
 | 
			
		||||
            y = self.randn(dj, dk)
 | 
			
		||||
 | 
			
		||||
            res = torch.hsmm(x, y)
 | 
			
		||||
            expected = torch.mm(x.to_dense(), y)
 | 
			
		||||
            self.assertEqual(res.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        test_shape(7, 5, 3)
 | 
			
		||||
        test_shape(1000, 100, 100)
 | 
			
		||||
        test_shape(3000, 64, 300)
 | 
			
		||||
 | 
			
		||||
    def _test_spadd_shape(self, shape_i, shape_v=None):
 | 
			
		||||
        shape = shape_i + (shape_v or [])
 | 
			
		||||
        x, _, _ = self._gen_sparse(len(shape_i), 10, shape)
 | 
			
		||||
        y = self.randn(*shape)
 | 
			
		||||
        r = random.random()
 | 
			
		||||
 | 
			
		||||
        res = torch.add(y, r, x)
 | 
			
		||||
        expected = y + r * x.to_dense()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
        # Non contiguous dense tensor
 | 
			
		||||
        s = list(shape)
 | 
			
		||||
        s[0] = shape[-1]
 | 
			
		||||
        s[-1] = shape[0]
 | 
			
		||||
        y = self.randn(*s)
 | 
			
		||||
        y.transpose_(0, len(s) - 1)
 | 
			
		||||
        r = random.random()
 | 
			
		||||
 | 
			
		||||
        res = torch.add(y, r, x)
 | 
			
		||||
        expected = y + r * x.to_dense()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
    def test_spadd(self):
 | 
			
		||||
        self._test_spadd_shape([5, 6])
 | 
			
		||||
        self._test_spadd_shape([10, 10, 10])
 | 
			
		||||
        self._test_spadd_shape([50, 30, 20])
 | 
			
		||||
        self._test_spadd_shape([5, 5, 5, 5, 5, 5])
 | 
			
		||||
 | 
			
		||||
    def test_spadd_hybrid(self):
 | 
			
		||||
        self._test_spadd_shape([5, 6], [2, 3])
 | 
			
		||||
        self._test_spadd_shape([10, 10, 10], [3])
 | 
			
		||||
        self._test_spadd_shape([50, 30, 20], [2])
 | 
			
		||||
        self._test_spadd_shape([5, 5, 5, 5, 5, 5], [2])
 | 
			
		||||
 | 
			
		||||
    def _test_basic_ops_shape(self, shape_i, shape_v=None):
 | 
			
		||||
        shape = shape_i + (shape_v or [])
 | 
			
		||||
        x1, _, _ = self._gen_sparse(len(shape_i), 9, shape)
 | 
			
		||||
        x2, _, _ = self._gen_sparse(len(shape_i), 12, shape)
 | 
			
		||||
 | 
			
		||||
        y1 = x1 + x2
 | 
			
		||||
        y2 = x1.clone()
 | 
			
		||||
        y2.add_(x2)
 | 
			
		||||
        expected = x1.to_dense() + x2.to_dense()
 | 
			
		||||
        self.assertEqual(y1.to_dense(), expected)
 | 
			
		||||
        self.assertEqual(y2.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        y1 = x1 - x2
 | 
			
		||||
        y2 = x1.clone()
 | 
			
		||||
        y2.sub_(x2)
 | 
			
		||||
        expected = x1.to_dense() - x2.to_dense()
 | 
			
		||||
        self.assertEqual(y1.to_dense(), expected)
 | 
			
		||||
        self.assertEqual(y2.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        y1 = x1 * x2
 | 
			
		||||
        y2 = x1.clone()
 | 
			
		||||
        y2.mul_(x2)
 | 
			
		||||
        expected = x1.to_dense() * x2.to_dense()
 | 
			
		||||
        self.assertEqual(y1.to_dense(), expected)
 | 
			
		||||
        self.assertEqual(y2.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        y1 = x1 * 37.5
 | 
			
		||||
        y2 = x1.clone()
 | 
			
		||||
        y2.mul_(37.5)
 | 
			
		||||
        expected = x1.to_dense() * 37.5
 | 
			
		||||
        self.assertEqual(y1.to_dense(), expected)
 | 
			
		||||
        self.assertEqual(y2.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        y1 = x1 / 37.5
 | 
			
		||||
        y2 = x1.clone()
 | 
			
		||||
        y2.div_(37.5)
 | 
			
		||||
        expected = x1.to_dense() / 37.5
 | 
			
		||||
        self.assertEqual(y1.to_dense(), expected)
 | 
			
		||||
        self.assertEqual(y2.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        # TODO: add back inplace support
 | 
			
		||||
        y1 = x1 ** 2
 | 
			
		||||
        y2 = x1.clone()
 | 
			
		||||
        y2 = y2.pow(2)
 | 
			
		||||
        expected = x1.to_dense() ** 2
 | 
			
		||||
        self.assertEqual(y1.to_dense(), expected)
 | 
			
		||||
        self.assertEqual(y2.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        y = x1.clone()
 | 
			
		||||
        y.zero_()
 | 
			
		||||
        expected = torch.zeros(x1.size())
 | 
			
		||||
        self.assertEqual(y.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(x1.is_coalesced())
 | 
			
		||||
        y = x1.coalesce()
 | 
			
		||||
        z = x1.coalesce()
 | 
			
		||||
        self.assertFalse(x1.is_coalesced())
 | 
			
		||||
        self.assertTrue(y.is_coalesced())
 | 
			
		||||
        self.assertEqual(x1, y)
 | 
			
		||||
        # check that coalesce is out of place
 | 
			
		||||
        y._values().add_(1)
 | 
			
		||||
        self.assertEqual(z._values() + 1, y._values())
 | 
			
		||||
 | 
			
		||||
    def test_basic_ops(self):
 | 
			
		||||
        self._test_basic_ops_shape([5, 6])
 | 
			
		||||
        self._test_basic_ops_shape([10, 10, 10])
 | 
			
		||||
        self._test_basic_ops_shape([50, 30, 20])
 | 
			
		||||
        self._test_basic_ops_shape([5, 5, 5, 5, 5, 5])
 | 
			
		||||
 | 
			
		||||
    def test_basic_ops_hybrid(self):
 | 
			
		||||
        self._test_basic_ops_shape([5, 6], [2, 3])
 | 
			
		||||
        self._test_basic_ops_shape([10, 10, 10], [3])
 | 
			
		||||
        self._test_basic_ops_shape([50, 30, 20], [2])
 | 
			
		||||
        self._test_basic_ops_shape([5, 5, 5, 5, 5, 5], [2])
 | 
			
		||||
 | 
			
		||||
    def _test_sparse_mask_shape(self, shape_i, shape_v=None):
 | 
			
		||||
        shape = shape_i + (shape_v or [])
 | 
			
		||||
        x1, _, _ = self._gen_sparse(len(shape_i), 9, shape)
 | 
			
		||||
        x2, _, _ = self._gen_sparse(len(shape_i), 12, shape)
 | 
			
		||||
 | 
			
		||||
        y1 = x1 + x2
 | 
			
		||||
        y2 = x1.clone()
 | 
			
		||||
        y2.add_(x2)
 | 
			
		||||
        expected = x1.to_dense() + x2.to_dense()
 | 
			
		||||
        self.assertEqual(y1.to_dense(), expected)
 | 
			
		||||
        self.assertEqual(y2.to_dense(), expected)
 | 
			
		||||
 | 
			
		||||
    def _test_sparse_mask_fixed(self):
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [1, 3, 0, 4],
 | 
			
		||||
            [2, 1, 2, 3],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([1, 2, 3, 4])
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([5, 4])).coalesce()
 | 
			
		||||
        dense = self.ValueTensor([
 | 
			
		||||
            [1, 2, 3, 4],
 | 
			
		||||
            [5, 6, 7, 8],
 | 
			
		||||
            [9, 10, 11, 12],
 | 
			
		||||
            [13, 14, 15, 16],
 | 
			
		||||
            [17, 18, 19, 20],
 | 
			
		||||
        ])
 | 
			
		||||
        exp_v = self.ValueTensor([7, 14, 3, 20])
 | 
			
		||||
        res = dense._sparse_mask(x)
 | 
			
		||||
        expected = self.SparseTensor(i, exp_v, torch.Size([5, 4]))
 | 
			
		||||
        self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
    def test_sparse_mask(self):
 | 
			
		||||
        self._test_sparse_mask_fixed()
 | 
			
		||||
 | 
			
		||||
        self._test_sparse_mask_shape([5, 6])
 | 
			
		||||
        self._test_sparse_mask_shape([10, 10, 10])
 | 
			
		||||
        self._test_sparse_mask_shape([50, 30, 20])
 | 
			
		||||
        self._test_sparse_mask_shape([5, 5, 5, 5, 5, 5])
 | 
			
		||||
 | 
			
		||||
    def _test_sparse_mask_hybrid_fixed(self):
 | 
			
		||||
        i = self.IndexTensor([
 | 
			
		||||
            [1, 3, 0, 4],
 | 
			
		||||
            [2, 1, 2, 3],
 | 
			
		||||
        ])
 | 
			
		||||
        v = self.ValueTensor([[1, 2], [2, 3], [3, 4], [4, 5]])
 | 
			
		||||
        # TODO: This is also testing that, if coalesce is a no-op,
 | 
			
		||||
        # the indices don't get permuted. I don't know if we actually
 | 
			
		||||
        # want to give this invariant.
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([5, 4, 2])).coalesce()
 | 
			
		||||
        dense = self.ValueTensor([
 | 
			
		||||
            [[1, 3], [2, 2], [3, 3], [4, 2]],
 | 
			
		||||
            [[5, 7], [6, 7], [7, 9], [8, 9]],
 | 
			
		||||
            [[9, 2], [10, 4], [11, 1], [12, 3]],
 | 
			
		||||
            [[13, 5], [14, 1], [15, 1], [16, 6]],
 | 
			
		||||
            [[17, 7], [18, 2], [19, 7], [20, 1]],
 | 
			
		||||
        ])
 | 
			
		||||
        res = dense._sparse_mask(x)
 | 
			
		||||
        exp_v = self.ValueTensor([[7, 9], [14, 1], [3, 3], [20, 1]])
 | 
			
		||||
        expected = self.SparseTensor(i, exp_v, torch.Size([5, 4, 2]))
 | 
			
		||||
        self.assertEqual(res, expected)
 | 
			
		||||
 | 
			
		||||
    def test_sparse_mask_hybrid(self):
 | 
			
		||||
        self._test_sparse_mask_hybrid_fixed()
 | 
			
		||||
 | 
			
		||||
        self._test_sparse_mask_shape([5, 6], [2, 3])
 | 
			
		||||
        self._test_sparse_mask_shape([10, 10, 10], [3])
 | 
			
		||||
        self._test_sparse_mask_shape([50, 30, 20], [2])
 | 
			
		||||
        self._test_sparse_mask_shape([5, 5, 5, 5, 5, 5], [2])
 | 
			
		||||
 | 
			
		||||
    @cuda_only
 | 
			
		||||
    def test_storage_not_null(self):
 | 
			
		||||
        x = torch.cuda.sparse.FloatTensor(2)
 | 
			
		||||
        self.assertNotEqual(x.get_device(), -1)
 | 
			
		||||
 | 
			
		||||
    @cuda_only
 | 
			
		||||
    @unittest.skipIf(torch.cuda.device_count() < 2, "only one GPU detected")
 | 
			
		||||
    def test_same_gpu(self):
 | 
			
		||||
        i = self.IndexTensor([[2]]).cuda(1)
 | 
			
		||||
        v = self.ValueTensor([5]).cuda(1)
 | 
			
		||||
        x = self.SparseTensor(i, v, torch.Size([3]), device=1)
 | 
			
		||||
        self.assertEqual(x.get_device(), 1)
 | 
			
		||||
        self.assertEqual(x._values().get_device(), 1)
 | 
			
		||||
        self.assertEqual(x._indices().get_device(), 1)
 | 
			
		||||
 | 
			
		||||
        x = self.SparseTensor(3, device=1)
 | 
			
		||||
        self.assertEqual(x.get_device(), 1)
 | 
			
		||||
        self.assertEqual(x._values().get_device(), 1)
 | 
			
		||||
        self.assertEqual(x._indices().get_device(), 1)
 | 
			
		||||
 | 
			
		||||
        v = self.ValueTensor([5]).cuda(0)
 | 
			
		||||
        self.assertRaises(RuntimeError, lambda: self.SparseTensor(i, v, torch.Size([3])))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestUncoalescedSparse(TestSparse):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestUncoalescedSparse, self).setUp()
 | 
			
		||||
        self.is_uncoalesced = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@unittest.skipIf(not TEST_CUDA, 'CUDA not available')
 | 
			
		||||
class TestCudaSparse(TestSparse):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestCudaSparse, self).setUp()
 | 
			
		||||
        self.is_cuda = True
 | 
			
		||||
        self.IndexTensor = torch.cuda.LongTensor
 | 
			
		||||
        self.ValueTensor = torch.cuda.DoubleTensor
 | 
			
		||||
        self.SparseTensor = torch.cuda.sparse.DoubleTensor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@unittest.skipIf(not TEST_CUDA, 'CUDA not available')
 | 
			
		||||
class TestCudaUncoalescedSparse(TestCudaSparse):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestCudaUncoalescedSparse, self).setUp()
 | 
			
		||||
        self.is_uncoalesced = True
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    run_tests()
 | 
			
		||||
							
								
								
									
										4229
									
								
								test/test_torch.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4229
									
								
								test/test_torch.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										384
									
								
								test/test_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								test/test_utils.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,384 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import math
 | 
			
		||||
import shutil
 | 
			
		||||
import random
 | 
			
		||||
import tempfile
 | 
			
		||||
import unittest
 | 
			
		||||
import traceback
 | 
			
		||||
import torch
 | 
			
		||||
import torch.utils.data
 | 
			
		||||
import torch.cuda
 | 
			
		||||
import warnings
 | 
			
		||||
from torch.autograd import Variable
 | 
			
		||||
from torch.utils.trainer import Trainer
 | 
			
		||||
from torch.utils.trainer.plugins import *
 | 
			
		||||
from torch.utils.trainer.plugins.plugin import Plugin
 | 
			
		||||
from torch.utils.serialization import load_lua
 | 
			
		||||
 | 
			
		||||
HAS_CUDA = torch.cuda.is_available()
 | 
			
		||||
 | 
			
		||||
from common import TestCase, run_tests, download_file
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import cffi
 | 
			
		||||
    from torch.utils.ffi import compile_extension
 | 
			
		||||
    HAS_CFFI = True
 | 
			
		||||
except ImportError:
 | 
			
		||||
    HAS_CFFI = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SimplePlugin(Plugin):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, interval):
 | 
			
		||||
        super(SimplePlugin, self).__init__(interval)
 | 
			
		||||
        self.trainer = None
 | 
			
		||||
        self.num_iteration = 0
 | 
			
		||||
        self.num_epoch = 0
 | 
			
		||||
        self.num_batch = 0
 | 
			
		||||
        self.num_update = 0
 | 
			
		||||
 | 
			
		||||
    def register(self, trainer):
 | 
			
		||||
        self.trainer = trainer
 | 
			
		||||
 | 
			
		||||
    def iteration(self, *args):
 | 
			
		||||
        self.iteration_args = args
 | 
			
		||||
        self.num_iteration += 1
 | 
			
		||||
 | 
			
		||||
    def epoch(self, *args):
 | 
			
		||||
        self.epoch_args = args
 | 
			
		||||
        self.num_epoch += 1
 | 
			
		||||
 | 
			
		||||
    def batch(self, *args):
 | 
			
		||||
        self.batch_args = args
 | 
			
		||||
        self.num_batch += 1
 | 
			
		||||
 | 
			
		||||
    def update(self, *args):
 | 
			
		||||
        self.update_args = args
 | 
			
		||||
        self.num_update += 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ModelMock(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.num_calls = 0
 | 
			
		||||
        self.output = Variable(torch.ones(1, 1), requires_grad=True)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, i):
 | 
			
		||||
        self.num_calls += 1
 | 
			
		||||
        return self.output * 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CriterionMock(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.num_calls = 0
 | 
			
		||||
 | 
			
		||||
    def __call__(self, out, target):
 | 
			
		||||
        self.num_calls += 1
 | 
			
		||||
        return out
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OptimizerMock(object):
 | 
			
		||||
    max_evals = 5
 | 
			
		||||
    min_evals = 1
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.num_steps = 0
 | 
			
		||||
        self.num_evals = 0
 | 
			
		||||
 | 
			
		||||
    def step(self, closure):
 | 
			
		||||
        for i in range(random.randint(self.min_evals, self.max_evals)):
 | 
			
		||||
            loss = closure()
 | 
			
		||||
            self.num_evals += 1
 | 
			
		||||
        self.num_steps += 1
 | 
			
		||||
 | 
			
		||||
    def zero_grad(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DatasetMock(object):
 | 
			
		||||
 | 
			
		||||
    def __iter__(self):
 | 
			
		||||
        for i in range(10):
 | 
			
		||||
            yield torch.randn(2, 10), torch.randperm(10)[:2]
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return 10
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDataLoader(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.dataset = torch.randn(5, 3, 3, 2)
 | 
			
		||||
        self.batch_size = 3
 | 
			
		||||
 | 
			
		||||
    def test_single_keep(self):
 | 
			
		||||
        dataloader = torch.utils.data.DataLoader(self.dataset,
 | 
			
		||||
                                                 batch_size=self.batch_size,
 | 
			
		||||
                                                 num_workers=0,
 | 
			
		||||
                                                 drop_last=False)
 | 
			
		||||
        dataiter = iter(dataloader)
 | 
			
		||||
        self.assertEqual(len(list(dataiter)), 2)
 | 
			
		||||
 | 
			
		||||
    def test_single_drop(self):
 | 
			
		||||
        dataloader = torch.utils.data.DataLoader(self.dataset,
 | 
			
		||||
                                                 batch_size=self.batch_size,
 | 
			
		||||
                                                 num_workers=0,
 | 
			
		||||
                                                 drop_last=True)
 | 
			
		||||
        dataiter = iter(dataloader)
 | 
			
		||||
        self.assertEqual(len(list(dataiter)), 1)
 | 
			
		||||
 | 
			
		||||
    def test_multi_keep(self):
 | 
			
		||||
        dataloader = torch.utils.data.DataLoader(self.dataset,
 | 
			
		||||
                                                 batch_size=self.batch_size,
 | 
			
		||||
                                                 num_workers=2,
 | 
			
		||||
                                                 drop_last=False)
 | 
			
		||||
        dataiter = iter(dataloader)
 | 
			
		||||
        self.assertEqual(len(list(dataiter)), 2)
 | 
			
		||||
 | 
			
		||||
    def test_multi_drop(self):
 | 
			
		||||
        dataloader = torch.utils.data.DataLoader(self.dataset,
 | 
			
		||||
                                                 batch_size=self.batch_size,
 | 
			
		||||
                                                 num_workers=2,
 | 
			
		||||
                                                 drop_last=True)
 | 
			
		||||
        dataiter = iter(dataloader)
 | 
			
		||||
        self.assertEqual(len(list(dataiter)), 1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestTrainer(TestCase):
 | 
			
		||||
 | 
			
		||||
    intervals = [
 | 
			
		||||
        [(1, 'iteration')],
 | 
			
		||||
        [(1, 'epoch')],
 | 
			
		||||
        [(1, 'batch')],
 | 
			
		||||
        [(1, 'update')],
 | 
			
		||||
        [(5, 'iteration')],
 | 
			
		||||
        [(5, 'epoch')],
 | 
			
		||||
        [(5, 'batch')],
 | 
			
		||||
        [(5, 'update')],
 | 
			
		||||
        [(1, 'iteration'), (1, 'epoch')],
 | 
			
		||||
        [(5, 'update'), (1, 'iteration')],
 | 
			
		||||
        [(2, 'epoch'), (1, 'batch')],
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.optimizer = OptimizerMock()
 | 
			
		||||
        self.trainer = Trainer(ModelMock(), CriterionMock(),
 | 
			
		||||
                               self.optimizer, DatasetMock())
 | 
			
		||||
        self.num_epochs = 3
 | 
			
		||||
        self.dataset_size = len(self.trainer.dataset)
 | 
			
		||||
        self.num_iters = self.num_epochs * self.dataset_size
 | 
			
		||||
 | 
			
		||||
    def test_register_plugin(self):
 | 
			
		||||
        for interval in self.intervals:
 | 
			
		||||
            simple_plugin = SimplePlugin(interval)
 | 
			
		||||
            self.trainer.register_plugin(simple_plugin)
 | 
			
		||||
            self.assertEqual(simple_plugin.trainer, self.trainer)
 | 
			
		||||
 | 
			
		||||
    def test_optimizer_step(self):
 | 
			
		||||
        self.trainer.run(epochs=1)
 | 
			
		||||
        self.assertEqual(self.trainer.optimizer.num_steps, 10)
 | 
			
		||||
 | 
			
		||||
    def test_plugin_interval(self):
 | 
			
		||||
        for interval in self.intervals:
 | 
			
		||||
            self.setUp()
 | 
			
		||||
            simple_plugin = SimplePlugin(interval)
 | 
			
		||||
            self.trainer.register_plugin(simple_plugin)
 | 
			
		||||
            self.trainer.run(epochs=self.num_epochs)
 | 
			
		||||
            units = {
 | 
			
		||||
                ('iteration', self.num_iters),
 | 
			
		||||
                ('epoch', self.num_epochs),
 | 
			
		||||
                ('batch', self.num_iters),
 | 
			
		||||
                ('update', self.num_iters)
 | 
			
		||||
            }
 | 
			
		||||
            for unit, num_triggers in units:
 | 
			
		||||
                call_every = None
 | 
			
		||||
                for i, i_unit in interval:
 | 
			
		||||
                    if i_unit == unit:
 | 
			
		||||
                        call_every = i
 | 
			
		||||
                        break
 | 
			
		||||
                if call_every:
 | 
			
		||||
                    expected_num_calls = math.floor(num_triggers / call_every)
 | 
			
		||||
                else:
 | 
			
		||||
                    expected_num_calls = 0
 | 
			
		||||
                num_calls = getattr(simple_plugin, 'num_' + unit)
 | 
			
		||||
                self.assertEqual(num_calls, expected_num_calls, 0)
 | 
			
		||||
 | 
			
		||||
    def test_model_called(self):
 | 
			
		||||
        self.trainer.run(epochs=self.num_epochs)
 | 
			
		||||
        num_model_calls = self.trainer.model.num_calls
 | 
			
		||||
        num_crit_calls = self.trainer.criterion.num_calls
 | 
			
		||||
        self.assertEqual(num_model_calls, num_crit_calls)
 | 
			
		||||
        for num_calls in [num_model_calls, num_crit_calls]:
 | 
			
		||||
            lower_bound = OptimizerMock.min_evals * self.num_iters
 | 
			
		||||
            upper_bound = OptimizerMock.max_evals * self.num_iters
 | 
			
		||||
            self.assertEqual(num_calls, self.trainer.optimizer.num_evals)
 | 
			
		||||
            self.assertLessEqual(lower_bound, num_calls)
 | 
			
		||||
            self.assertLessEqual(num_calls, upper_bound)
 | 
			
		||||
 | 
			
		||||
    def test_model_gradient(self):
 | 
			
		||||
        self.trainer.run(epochs=self.num_epochs)
 | 
			
		||||
        output_var = self.trainer.model.output
 | 
			
		||||
        expected_grad = torch.ones(1, 1) * 2 * self.optimizer.num_evals
 | 
			
		||||
        self.assertEqual(output_var.grad.data, expected_grad)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
test_dir = os.path.abspath(os.path.dirname(str(__file__)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestFFI(TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.tmpdir = tempfile.mkdtemp()
 | 
			
		||||
        os.chdir(self.tmpdir)
 | 
			
		||||
        sys.path.append(self.tmpdir)
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        shutil.rmtree(self.tmpdir)
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not HAS_CFFI, "ffi tests require cffi package")
 | 
			
		||||
    def test_cpu(self):
 | 
			
		||||
        compile_extension(
 | 
			
		||||
            name='test_extensions.cpulib',
 | 
			
		||||
            header=test_dir + '/ffi/src/cpu/lib.h',
 | 
			
		||||
            sources=[
 | 
			
		||||
                test_dir + '/ffi/src/cpu/lib1.c',
 | 
			
		||||
                test_dir + '/ffi/src/cpu/lib2.c',
 | 
			
		||||
            ],
 | 
			
		||||
            verbose=False,
 | 
			
		||||
        )
 | 
			
		||||
        from test_extensions import cpulib
 | 
			
		||||
        tensor = torch.ones(2, 2).float()
 | 
			
		||||
 | 
			
		||||
        cpulib.good_func(tensor, 2, 1.5)
 | 
			
		||||
        self.assertEqual(tensor, torch.ones(2, 2) * 2 + 1.5)
 | 
			
		||||
 | 
			
		||||
        new_tensor = cpulib.new_tensor(4)
 | 
			
		||||
        self.assertEqual(new_tensor, torch.ones(4, 4) * 4)
 | 
			
		||||
 | 
			
		||||
        f = cpulib.int_to_float(5)
 | 
			
		||||
        self.assertIs(type(f), float)
 | 
			
		||||
 | 
			
		||||
        self.assertRaises(TypeError,
 | 
			
		||||
                          lambda: cpulib.good_func(tensor.double(), 2, 1.5))
 | 
			
		||||
        self.assertRaises(torch.FatalError,
 | 
			
		||||
                          lambda: cpulib.bad_func(tensor, 2, 1.5))
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(not HAS_CFFI or not HAS_CUDA, "ffi tests require cffi package")
 | 
			
		||||
    def test_gpu(self):
 | 
			
		||||
        compile_extension(
 | 
			
		||||
            name='gpulib',
 | 
			
		||||
            header=test_dir + '/ffi/src/cuda/cudalib.h',
 | 
			
		||||
            sources=[
 | 
			
		||||
                test_dir + '/ffi/src/cuda/cudalib.c',
 | 
			
		||||
            ],
 | 
			
		||||
            with_cuda=True,
 | 
			
		||||
            verbose=False,
 | 
			
		||||
        )
 | 
			
		||||
        import gpulib
 | 
			
		||||
        tensor = torch.ones(2, 2).float()
 | 
			
		||||
 | 
			
		||||
        gpulib.good_func(tensor, 2, 1.5)
 | 
			
		||||
        self.assertEqual(tensor, torch.ones(2, 2) * 2 + 1.5)
 | 
			
		||||
 | 
			
		||||
        ctensor = tensor.cuda().fill_(1)
 | 
			
		||||
        gpulib.cuda_func(ctensor, 2, 1.5)
 | 
			
		||||
        self.assertEqual(ctensor, torch.ones(2, 2) * 2 + 1.5)
 | 
			
		||||
 | 
			
		||||
        self.assertRaises(TypeError,
 | 
			
		||||
                          lambda: gpulib.cuda_func(tensor, 2, 1.5))
 | 
			
		||||
        self.assertRaises(TypeError,
 | 
			
		||||
                          lambda: gpulib.cuda_func(ctensor.storage(), 2, 1.5))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestLuaReader(TestCase):
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _module_test(name, test):
 | 
			
		||||
        def do_test(self):
 | 
			
		||||
            module = test['module']
 | 
			
		||||
            input = test['input']
 | 
			
		||||
            grad_output = test['grad_output']
 | 
			
		||||
            if hasattr(self, '_transform_' + name):
 | 
			
		||||
                input = getattr(self, '_transform_' + name)(input)
 | 
			
		||||
            output = module.forward(input)
 | 
			
		||||
            module.zeroGradParameters()
 | 
			
		||||
            grad_input = module.backward(input, grad_output)
 | 
			
		||||
            self.assertEqual(output, test['output'])
 | 
			
		||||
            self.assertEqual(grad_input, test['grad_input'])
 | 
			
		||||
            if module.parameters() is not None:
 | 
			
		||||
                params, d_params = module.parameters()
 | 
			
		||||
                self.assertEqual(params, test['params'])
 | 
			
		||||
                self.assertEqual(d_params, test['d_params'])
 | 
			
		||||
            else:
 | 
			
		||||
                self.assertFalse('params' in test and test['params'])
 | 
			
		||||
                self.assertFalse('params' in test and test['d_params'])
 | 
			
		||||
        return do_test
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _criterion_test(name, test):
 | 
			
		||||
        def do_test(self):
 | 
			
		||||
            module = test['module']
 | 
			
		||||
            input = test['input']
 | 
			
		||||
            if name == 'L1Cost':
 | 
			
		||||
                target = None
 | 
			
		||||
            else:
 | 
			
		||||
                target = test['target']
 | 
			
		||||
            if hasattr(self, '_transform_' + name):
 | 
			
		||||
                input, target = getattr(self, '_transform_' + name)(input, target)
 | 
			
		||||
 | 
			
		||||
            output = module.forward(input, target)
 | 
			
		||||
            grad_input = module.backward(input, target)
 | 
			
		||||
            self.assertEqual(output, test['loss'])
 | 
			
		||||
            self.assertEqual(grad_input, test['grad_input'])
 | 
			
		||||
        return do_test
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def init(cls):
 | 
			
		||||
        try:
 | 
			
		||||
            path = download_file('https://download.pytorch.org/test_data/legacy_modules.t7')
 | 
			
		||||
        except unittest.SkipTest:
 | 
			
		||||
            return
 | 
			
		||||
        tests = load_lua(path)
 | 
			
		||||
        for name, test in tests['modules'].items():
 | 
			
		||||
            test_name = 'test_' + name.replace('nn.', '')
 | 
			
		||||
            setattr(cls, test_name, cls._module_test(name, test))
 | 
			
		||||
        for name, test in tests['criterions'].items():
 | 
			
		||||
            test_name = 'test_' + name.replace('nn.', '')
 | 
			
		||||
            setattr(cls, test_name, cls._criterion_test(name, test))
 | 
			
		||||
 | 
			
		||||
    def _transform_Index(self, input):
 | 
			
		||||
        return [input[0], input[1].sub(1)]
 | 
			
		||||
 | 
			
		||||
    def _transform_LookupTable(self, input):
 | 
			
		||||
        return input.sub(1)
 | 
			
		||||
 | 
			
		||||
    def _transform_MultiLabelMarginCriterion(self, input, target):
 | 
			
		||||
        return input, target.sub(1)
 | 
			
		||||
 | 
			
		||||
    def _transform_ClassNLLCriterion(self, input, target):
 | 
			
		||||
        return input, target.sub(1)
 | 
			
		||||
 | 
			
		||||
    def _transform_SpatialClassNLLCriterion(self, input, target):
 | 
			
		||||
        return input, target.sub(1)
 | 
			
		||||
 | 
			
		||||
    def _transform_ClassSimplexCriterion(self, input, target):
 | 
			
		||||
        return input, target.sub(1)
 | 
			
		||||
 | 
			
		||||
    def _transform_CrossEntropyCriterion(self, input, target):
 | 
			
		||||
        return input, target.sub(1)
 | 
			
		||||
 | 
			
		||||
    def _transform_ParallelCriterion(self, input, target):
 | 
			
		||||
        return input, [target[0].sub(1), target[1]]
 | 
			
		||||
 | 
			
		||||
    def _transform_MultiCriterion(self, input, target):
 | 
			
		||||
        return input, target.sub(1)
 | 
			
		||||
 | 
			
		||||
    def _transform_MultiMarginCriterion(self, input, target):
 | 
			
		||||
        return input, target.sub(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TestLuaReader.init()
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    run_tests()
 | 
			
		||||
							
								
								
									
										0
									
								
								tools/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tools/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										52
									
								
								tools/convert.vim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								tools/convert.vim
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
"Slightly adjust indentation
 | 
			
		||||
%s/^   /        /g
 | 
			
		||||
 | 
			
		||||
" # -> len
 | 
			
		||||
%s/#\(\S*\) /len(\1)/g
 | 
			
		||||
 | 
			
		||||
" for loops
 | 
			
		||||
%s/for\( \)\{-\}\(\S*\)\( \)\{-\}=\( \)\{-\}\(\S*\),\( \)\{-\}\(\S*\)\( \)\{-\}do/for \2 in range(\5, \7+1)/g
 | 
			
		||||
 | 
			
		||||
" Change comments
 | 
			
		||||
%s/--\[\[/"""/g
 | 
			
		||||
%s/]]/"""/g
 | 
			
		||||
%s/--/#/g
 | 
			
		||||
 | 
			
		||||
" Add spacing between commas
 | 
			
		||||
%s/\(\S\),\(\S\)/\1, \2/g
 | 
			
		||||
 | 
			
		||||
%s/local //g
 | 
			
		||||
%s/ then/:/g
 | 
			
		||||
%s/ do/:/g
 | 
			
		||||
%s/end//g
 | 
			
		||||
%s/elseif/elif/g
 | 
			
		||||
%s/else/else:/g
 | 
			
		||||
%s/true/True/g
 | 
			
		||||
%s/false/False/g
 | 
			
		||||
%s/\~=/!=/g
 | 
			
		||||
%s/math\.min/min/g
 | 
			
		||||
%s/math\.max/max/g
 | 
			
		||||
%s/math\.abs/abs/g
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%s/__init/__init__/g
 | 
			
		||||
 | 
			
		||||
" Rewrite function declarations
 | 
			
		||||
%s/function \w*:\(\w*\)/    def \1/g
 | 
			
		||||
%s/def \(.*\)$/def \1:/g
 | 
			
		||||
 | 
			
		||||
" class declaration
 | 
			
		||||
%s/\(\w*\), parent = torch\.class.*$/import torch\rfrom torch.legacy import nn\r\rclass \1(nn.Module):/g
 | 
			
		||||
 | 
			
		||||
%s/input\.THNN/self._backend/g
 | 
			
		||||
%s/\(self\.backend\w*$\)/\1\r        self._backend.library_state,/g
 | 
			
		||||
%s/def \(\w*\)(/def \1(self, /g
 | 
			
		||||
 | 
			
		||||
%s/__init__(self)/__init__()/g
 | 
			
		||||
 | 
			
		||||
%s/:\(\S\)/.\1/g
 | 
			
		||||
 | 
			
		||||
%s/\.cdata()//g
 | 
			
		||||
%s/THNN\.optionalTensor(\(.*\))/\1/g
 | 
			
		||||
 | 
			
		||||
%s/parent\./super(##, self)./g
 | 
			
		||||
							
								
								
									
										1
									
								
								tools/cwrap/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tools/cwrap/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
from .cwrap import cwrap
 | 
			
		||||
							
								
								
									
										277
									
								
								tools/cwrap/cwrap.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								tools/cwrap/cwrap.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,277 @@
 | 
			
		||||
import os
 | 
			
		||||
import yaml
 | 
			
		||||
from string import Template
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from .plugins import ArgcountChecker, OptionalArguments, ArgumentReferences, \
 | 
			
		||||
    BeforeAfterCall, ConstantArguments, ReturnArguments, GILRelease
 | 
			
		||||
from ..shared import cwrap_common
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class cwrap(object):
 | 
			
		||||
    BASE_INDENT_SIZE = 6
 | 
			
		||||
 | 
			
		||||
    RETURN_WRAPPERS = {
 | 
			
		||||
        'void': Template('Py_RETURN_NONE;'),
 | 
			
		||||
        'long': Template('return PyLong_FromLong($result);'),
 | 
			
		||||
        'bool': Template('return PyBool_FromLong($result);'),
 | 
			
		||||
        'void*': Template('return PyLong_FromVoidPtr($result);'),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OPTION_TEMPLATE = Template("""
 | 
			
		||||
    ${els}if ($arg_check) {
 | 
			
		||||
      $pre_arg_assign
 | 
			
		||||
      $arg_assign
 | 
			
		||||
      $code
 | 
			
		||||
    """)
 | 
			
		||||
 | 
			
		||||
    ARG_ASSIGN_TEMPLATE = Template("""${type} ${name} = ${unpack};""")
 | 
			
		||||
 | 
			
		||||
    OPTION_CODE_TEMPLATE = [
 | 
			
		||||
        '$call',
 | 
			
		||||
        '$return_result',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    FUNCTION_CALL_TEMPLATE = Template("$capture_result$cname($call_arg);")
 | 
			
		||||
 | 
			
		||||
    DEFAULT_PLUGIN_CLASSES = [ArgcountChecker, ConstantArguments, OptionalArguments,
 | 
			
		||||
                              ArgumentReferences, BeforeAfterCall, ReturnArguments, GILRelease]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, source, destination=None, plugins=None, default_plugins=True):
 | 
			
		||||
        if destination is None:
 | 
			
		||||
            destination = source.replace('.cwrap', '.cpp')
 | 
			
		||||
 | 
			
		||||
        self.plugins = [] if plugins is None else plugins
 | 
			
		||||
        if default_plugins:
 | 
			
		||||
            defaults = [cls() for cls in self.DEFAULT_PLUGIN_CLASSES]
 | 
			
		||||
            self.plugins = defaults + self.plugins
 | 
			
		||||
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            plugin.initialize(self)
 | 
			
		||||
 | 
			
		||||
        self.base_path = os.path.dirname(os.path.abspath(source))
 | 
			
		||||
        with open(source, 'r') as f:
 | 
			
		||||
            declarations = f.read()
 | 
			
		||||
 | 
			
		||||
        # wrap all the declarations in the source .cwrap file
 | 
			
		||||
        wrapper = self.wrap_declarations(declarations)
 | 
			
		||||
 | 
			
		||||
        # let each plugin do any post-processing of the wrapped file
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            wrapper = plugin.process_full_file(wrapper)
 | 
			
		||||
 | 
			
		||||
        with open(destination, 'w') as f:
 | 
			
		||||
            f.write(wrapper)
 | 
			
		||||
 | 
			
		||||
    def wrap_declarations(self, declarations):
 | 
			
		||||
        lines = declarations.split('\n')
 | 
			
		||||
        declaration_lines = []
 | 
			
		||||
        output = []
 | 
			
		||||
        in_declaration = False
 | 
			
		||||
        i = 0
 | 
			
		||||
 | 
			
		||||
        while i < len(lines):
 | 
			
		||||
            line = lines[i]
 | 
			
		||||
            if line == '[[':
 | 
			
		||||
                declaration_lines = []
 | 
			
		||||
                in_declaration = True
 | 
			
		||||
            elif line == ']]':
 | 
			
		||||
                in_declaration = False
 | 
			
		||||
                declaration = yaml.load('\n'.join(declaration_lines))
 | 
			
		||||
                cwrap_common.set_declaration_defaults(declaration)
 | 
			
		||||
 | 
			
		||||
                # Pass declaration in a list - maybe some plugins want to add
 | 
			
		||||
                # multiple wrappers
 | 
			
		||||
                declarations = [declaration]
 | 
			
		||||
                for plugin in self.plugins:
 | 
			
		||||
                    declarations = plugin.process_declarations(declarations)
 | 
			
		||||
                # Generate wrappers for all declarations and append them to
 | 
			
		||||
                # the output
 | 
			
		||||
                for declaration in declarations:
 | 
			
		||||
                    wrapper = self.generate_wrapper(declaration)
 | 
			
		||||
                    for plugin in self.plugins:
 | 
			
		||||
                        wrapper = plugin.process_wrapper(wrapper, declaration)
 | 
			
		||||
                    output.append(wrapper)
 | 
			
		||||
            elif in_declaration:
 | 
			
		||||
                declaration_lines.append(line)
 | 
			
		||||
            elif '!!inc ' == line[:6]:
 | 
			
		||||
                fname = os.path.join(self.base_path, line[6:].strip())
 | 
			
		||||
                with open(fname, 'r') as f:
 | 
			
		||||
                    included = f.read().split('\n')
 | 
			
		||||
                # insert it into lines at position i+1
 | 
			
		||||
                lines[i + 1:i + 1] = included
 | 
			
		||||
            else:
 | 
			
		||||
                output.append(line)
 | 
			
		||||
            i += 1
 | 
			
		||||
 | 
			
		||||
        return '\n'.join(output)
 | 
			
		||||
 | 
			
		||||
    def parse_arguments(self, args):
 | 
			
		||||
        new_args = []
 | 
			
		||||
        for arg in args:
 | 
			
		||||
            # Simple arg declaration of form "<type> <name>"
 | 
			
		||||
            if isinstance(arg, str):
 | 
			
		||||
                t, _, name = arg.partition(' ')
 | 
			
		||||
                new_args.append({'type': t, 'name': name})
 | 
			
		||||
            elif isinstance(arg, dict):
 | 
			
		||||
                if 'arg' in arg:
 | 
			
		||||
                    arg['type'], _, arg['name'] = arg['arg'].partition(' ')
 | 
			
		||||
                    del arg['arg']
 | 
			
		||||
                new_args.append(arg)
 | 
			
		||||
            else:
 | 
			
		||||
                assert False
 | 
			
		||||
        return new_args
 | 
			
		||||
 | 
			
		||||
    def search_plugins(self, fnname, args, fallback):
 | 
			
		||||
        """Search plugins for the given function to call with args.
 | 
			
		||||
 | 
			
		||||
        If not found, call fallback with args.
 | 
			
		||||
        """
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            wrapper = getattr(plugin, fnname)(*args)
 | 
			
		||||
            if wrapper is not None:
 | 
			
		||||
                return wrapper
 | 
			
		||||
        return fallback(*args)
 | 
			
		||||
 | 
			
		||||
    def get_type_check(self, arg, option):
 | 
			
		||||
        return self.search_plugins('get_type_check', (arg, option), lambda arg, _: None)
 | 
			
		||||
 | 
			
		||||
    def get_type_unpack(self, arg, option):
 | 
			
		||||
        return self.search_plugins('get_type_unpack', (arg, option), lambda arg, _: None)
 | 
			
		||||
 | 
			
		||||
    def get_return_wrapper(self, option):
 | 
			
		||||
        return self.search_plugins('get_return_wrapper', (option,), lambda _: self.RETURN_WRAPPERS[option['return']])
 | 
			
		||||
 | 
			
		||||
    def get_wrapper_template(self, declaration):
 | 
			
		||||
        return self.search_plugins('get_wrapper_template', (declaration,), lambda _: None)
 | 
			
		||||
 | 
			
		||||
    def get_assign_args(self, arguments):
 | 
			
		||||
        return self.search_plugins('get_assign_args', (arguments,), lambda _: arguments)
 | 
			
		||||
 | 
			
		||||
    def get_arg_accessor(self, arg, option):
 | 
			
		||||
        def wrap_accessor(arg, _):
 | 
			
		||||
            if arg.get('idx') is None:
 | 
			
		||||
                raise RuntimeError("Missing accessor for '{} {}'".format(
 | 
			
		||||
                                   arg['type'], arg['name']))
 | 
			
		||||
            return 'PyTuple_GET_ITEM(args, {})'.format(arg['idx'])
 | 
			
		||||
 | 
			
		||||
        return self.search_plugins('get_arg_accessor', (arg, option), wrap_accessor)
 | 
			
		||||
 | 
			
		||||
    def generate_wrapper(self, declaration):
 | 
			
		||||
        wrapper = ''
 | 
			
		||||
        for i, option in enumerate(declaration['options']):
 | 
			
		||||
            option_wrapper = self.generate_option(option, is_first=(i == 0))
 | 
			
		||||
            for plugin in self.plugins:
 | 
			
		||||
                option_wrapper = plugin.process_option_code(option_wrapper, option)
 | 
			
		||||
            wrapper += option_wrapper
 | 
			
		||||
        return self.get_wrapper_template(declaration).substitute(name=declaration['name'], options=wrapper)
 | 
			
		||||
 | 
			
		||||
    def map_selected_arguments(self, base_fn_name, plugin_fn_name, option, arguments):
 | 
			
		||||
        result = []
 | 
			
		||||
        for arg in arguments:
 | 
			
		||||
            accessor = self.get_arg_accessor(arg, option)
 | 
			
		||||
            tmpl = getattr(self, base_fn_name)(arg, option)
 | 
			
		||||
            if tmpl is None:
 | 
			
		||||
                fn = 'check' if base_fn_name == 'get_type_check' else 'unpack'
 | 
			
		||||
                raise RuntimeError("Missing type {} for '{} {}'".format(
 | 
			
		||||
                                   fn, arg['type'], arg['name']))
 | 
			
		||||
            res = tmpl.substitute(arg=accessor, idx=arg.get('idx'))
 | 
			
		||||
            for plugin in self.plugins:
 | 
			
		||||
                res = getattr(plugin, plugin_fn_name)(res, arg, accessor)
 | 
			
		||||
 | 
			
		||||
            result.append(res)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def build_option_args(self, arguments, arg_unpack):
 | 
			
		||||
        assignement = []
 | 
			
		||||
        call_arg = []
 | 
			
		||||
        # If types or names needs to be changed
 | 
			
		||||
        arguments = self.get_assign_args(arguments)
 | 
			
		||||
        for arg, unpack in zip(arguments, arg_unpack):
 | 
			
		||||
            if arg['type'] == 'CONSTANT':
 | 
			
		||||
                call_arg.append(unpack)
 | 
			
		||||
            else:
 | 
			
		||||
                var_name = "arg_" + str(arg.get('assign_name', arg['name']))
 | 
			
		||||
                res = self.ARG_ASSIGN_TEMPLATE.substitute(
 | 
			
		||||
                    type=arg['type'],
 | 
			
		||||
                    name=var_name,
 | 
			
		||||
                    unpack=unpack)
 | 
			
		||||
 | 
			
		||||
                if var_name not in call_arg:
 | 
			
		||||
                    assignement.append(res)
 | 
			
		||||
                call_arg.append(var_name)
 | 
			
		||||
        return assignement, call_arg
 | 
			
		||||
 | 
			
		||||
    def indent_code(self, code):
 | 
			
		||||
        if code == '':
 | 
			
		||||
            return code
 | 
			
		||||
        code_lines = map(lambda s: s.strip(), code.split('\n'))
 | 
			
		||||
        code = '\n'
 | 
			
		||||
        depth = self.BASE_INDENT_SIZE
 | 
			
		||||
        for line in code_lines:
 | 
			
		||||
            depth -= line.count('}') * 2
 | 
			
		||||
            code += ' ' * depth + line + '\n'
 | 
			
		||||
            depth += line.count('{') * 2
 | 
			
		||||
            depth += line.count('(') * 4
 | 
			
		||||
            depth -= line.count(')') * 4
 | 
			
		||||
        return code[:-1]
 | 
			
		||||
 | 
			
		||||
    def generate_option(self, option, is_first):
 | 
			
		||||
        checked_args = list(filter(
 | 
			
		||||
            lambda arg: 'ignore_check' not in arg or not arg['ignore_check'],
 | 
			
		||||
            option['arguments']))
 | 
			
		||||
        option['num_checked_args'] = len(checked_args)
 | 
			
		||||
        idx_args = list(filter(
 | 
			
		||||
            lambda arg: not arg.get('ignore_check') and not arg.get('no_idx'),
 | 
			
		||||
            option['arguments']))
 | 
			
		||||
        for i, arg in enumerate(idx_args):
 | 
			
		||||
            arg['idx'] = i
 | 
			
		||||
 | 
			
		||||
        # Generate checks
 | 
			
		||||
        arg_checks = self.map_selected_arguments('get_type_check',
 | 
			
		||||
                                                 'process_single_check', option, checked_args)
 | 
			
		||||
        arg_checks = ' &&\n          '.join(arg_checks)
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            arg_checks = plugin.process_all_checks(arg_checks, option)
 | 
			
		||||
 | 
			
		||||
        # Generate pre_arg assign
 | 
			
		||||
        pre_arg_assign = []
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            pre_arg_assign = plugin.process_pre_arg_assign(pre_arg_assign, option)
 | 
			
		||||
 | 
			
		||||
        # Generate arg assignment and call arguments
 | 
			
		||||
        arg_unpack = self.map_selected_arguments('get_type_unpack',
 | 
			
		||||
                                                 'process_single_unpack', option, option['arguments'])
 | 
			
		||||
        arg_assign, call_arg = self.build_option_args(option['arguments'], arg_unpack)
 | 
			
		||||
 | 
			
		||||
        call_arg = ', '.join(call_arg)
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            call_arg = plugin.process_all_call_arg(call_arg, option)
 | 
			
		||||
 | 
			
		||||
        # Generate call
 | 
			
		||||
        try:
 | 
			
		||||
            return_result = self.get_return_wrapper(option).substitute()
 | 
			
		||||
            call = self.FUNCTION_CALL_TEMPLATE.substitute(capture_result='',
 | 
			
		||||
                                                          cname=option['cname'], call_arg=call_arg)
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            return_result = self.get_return_wrapper(option).substitute(result='__result')
 | 
			
		||||
            call = self.FUNCTION_CALL_TEMPLATE.substitute(capture_result=(option['return'] + ' __result = '),
 | 
			
		||||
                                                          cname=option['cname'], call_arg=call_arg)
 | 
			
		||||
 | 
			
		||||
        code_template = deepcopy(self.OPTION_CODE_TEMPLATE)
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            code_template = plugin.process_option_code_template(code_template,
 | 
			
		||||
                                                                option)
 | 
			
		||||
        code_template = Template('\n'.join(code_template))
 | 
			
		||||
        code = code_template.substitute(call=call, return_result=return_result)
 | 
			
		||||
        code = self.indent_code(code)
 | 
			
		||||
        pre_arg_assign = self.indent_code('\n'.join(pre_arg_assign))
 | 
			
		||||
        arg_assign = self.indent_code('\n'.join(arg_assign))
 | 
			
		||||
 | 
			
		||||
        # Put everything together
 | 
			
		||||
        return self.OPTION_TEMPLATE.substitute(
 | 
			
		||||
            els=('} else ' if not is_first else ''),
 | 
			
		||||
            arg_check=arg_checks,
 | 
			
		||||
            pre_arg_assign=pre_arg_assign,
 | 
			
		||||
            arg_assign=arg_assign,
 | 
			
		||||
            code=code,
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										13
									
								
								tools/cwrap/plugins/ArgcountChecker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tools/cwrap/plugins/ArgcountChecker.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ArgcountChecker(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def process_all_checks(self, checks, option):
 | 
			
		||||
        if not checks:
 | 
			
		||||
            checks = '__argcount == 0'
 | 
			
		||||
        else:
 | 
			
		||||
            indent = '\n          '
 | 
			
		||||
            argcount = option['num_checked_args'] + option.get('argcount_offset', 0)
 | 
			
		||||
            checks = '__argcount == {} &&'.format(str(argcount)) + indent + checks
 | 
			
		||||
        return checks
 | 
			
		||||
							
								
								
									
										15
									
								
								tools/cwrap/plugins/ArgcountSortPlugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tools/cwrap/plugins/ArgcountSortPlugin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
import os
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from ...shared import cwrap_common
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ArgcountSortPlugin(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, descending=True):
 | 
			
		||||
        self.descending = descending
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            cwrap_common.sort_by_number_of_options(declaration,
 | 
			
		||||
                                                   self.descending)
 | 
			
		||||
        return declarations
 | 
			
		||||
							
								
								
									
										29
									
								
								tools/cwrap/plugins/ArgumentReferences.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tools/cwrap/plugins/ArgumentReferences.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ArgumentReferences(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def initialize(self, cwrap):
 | 
			
		||||
        self.cwrap = cwrap
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            for option in declaration['options']:
 | 
			
		||||
                for arg in option['arguments']:
 | 
			
		||||
                    if arg['type'] == 'argument':
 | 
			
		||||
                        arg['ignore_check'] = True
 | 
			
		||||
                        arg['is_reference'] = True
 | 
			
		||||
                        # Copy type from referenced argument
 | 
			
		||||
                        idx = int(arg['name'])
 | 
			
		||||
                        arg['type'] = option['arguments'][idx]['type']
 | 
			
		||||
        return declarations
 | 
			
		||||
 | 
			
		||||
    def _get_true_idx(self, idx, option):
 | 
			
		||||
        return sum(not arg.get('ignore_check', False) for arg in option['arguments'][:idx])
 | 
			
		||||
 | 
			
		||||
    def get_arg_accessor(self, arg, option):
 | 
			
		||||
        if arg.get('is_reference', False):
 | 
			
		||||
            idx = int(arg['name'])
 | 
			
		||||
            referenced = option['arguments'][idx]
 | 
			
		||||
            return self.cwrap.get_arg_accessor(referenced, option)
 | 
			
		||||
							
								
								
									
										29
									
								
								tools/cwrap/plugins/AssertNDim.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tools/cwrap/plugins/AssertNDim.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssertNDim(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    PRE_CODE_TEMPLATE = Template(
 | 
			
		||||
        """if(THTensor_(nDimension)(LIBRARY_STATE ${arg_op}) != ${dim_value}) {
 | 
			
		||||
             THError("Expected argument %s to have %d dimension(s), but has %d",
 | 
			
		||||
                     "${op}", ${dim_value}, THTensor_(nDimension)(LIBRARY_STATE ${arg_op}));
 | 
			
		||||
           }
 | 
			
		||||
        """)
 | 
			
		||||
 | 
			
		||||
    def process_option_code_template(self, template, option):
 | 
			
		||||
        new_code_pre = []
 | 
			
		||||
 | 
			
		||||
        for _, arg in enumerate(option['arguments']):
 | 
			
		||||
            if 'assert_ndim' not in arg:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            dim_value = arg.get('assert_ndim')
 | 
			
		||||
            op = arg.get('assign_name', arg['name'])
 | 
			
		||||
            arg_op = "arg_" + op
 | 
			
		||||
            new_code_pre.append(self.PRE_CODE_TEMPLATE.substitute(op=op,
 | 
			
		||||
                                                                  arg_op=arg_op,
 | 
			
		||||
                                                                  dim_value=dim_value))
 | 
			
		||||
            template = new_code_pre + template
 | 
			
		||||
 | 
			
		||||
        return template
 | 
			
		||||
							
								
								
									
										30
									
								
								tools/cwrap/plugins/AutoGPU.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tools/cwrap/plugins/AutoGPU.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AutoGPU(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, has_self=True, condition=None):
 | 
			
		||||
        self.has_self = has_self
 | 
			
		||||
        self.condition = condition
 | 
			
		||||
 | 
			
		||||
    DEFINES = """
 | 
			
		||||
#ifdef THC_GENERIC_FILE
 | 
			
		||||
#define THCP_AUTO_GPU 1
 | 
			
		||||
#else
 | 
			
		||||
#define THCP_AUTO_GPU 0
 | 
			
		||||
#endif
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    def process_pre_arg_assign(self, template, option):
 | 
			
		||||
        if not option.get('auto_gpu', True):
 | 
			
		||||
            return template
 | 
			
		||||
        call = 'THCPAutoGPU __autogpu_guard = THCPAutoGPU(args{});'.format(
 | 
			
		||||
            ', (PyObject*)self' if self.has_self else '')
 | 
			
		||||
 | 
			
		||||
        if self.condition is not None:
 | 
			
		||||
            call = "#if {0}\n      {1}\n#endif\n".format(self.condition, call)
 | 
			
		||||
 | 
			
		||||
        return [call] + template
 | 
			
		||||
 | 
			
		||||
    def process_full_file(self, code):
 | 
			
		||||
        return self.DEFINES + code
 | 
			
		||||
							
								
								
									
										33
									
								
								tools/cwrap/plugins/BeforeAfterCall.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tools/cwrap/plugins/BeforeAfterCall.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BeforeAfterCall(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def initialize(self, cwrap):
 | 
			
		||||
        self.cwrap = cwrap
 | 
			
		||||
 | 
			
		||||
    def insert_snippet(self, template, option, offset, name):
 | 
			
		||||
        prepend_str = option.get(name)
 | 
			
		||||
        if prepend_str is None:
 | 
			
		||||
            return
 | 
			
		||||
        if '$' in prepend_str:
 | 
			
		||||
            before_call_template = Template(option[name])
 | 
			
		||||
            args = {'arg' + str(i): self.cwrap.get_arg_accessor(arg, option) for i, arg
 | 
			
		||||
                    in enumerate(option['arguments'])}
 | 
			
		||||
            prepend_str = before_call_template.substitute(args)
 | 
			
		||||
        template.insert(offset, prepend_str)
 | 
			
		||||
 | 
			
		||||
    def process_pre_arg_assign(self, template, option):
 | 
			
		||||
        if option.get('before_arg_assign'):
 | 
			
		||||
            self.insert_snippet(template, option, 0, 'before_arg_assign')
 | 
			
		||||
        return template
 | 
			
		||||
 | 
			
		||||
    def process_option_code_template(self, template, option):
 | 
			
		||||
        if option.get('before_call') or option.get('after_call'):
 | 
			
		||||
            call_idx = template.index('$call')
 | 
			
		||||
            self.insert_snippet(template, option, call_idx, 'before_call')
 | 
			
		||||
            # call position might have changed
 | 
			
		||||
            call_idx = template.index('$call')
 | 
			
		||||
            self.insert_snippet(template, option, call_idx + 1, 'after_call')
 | 
			
		||||
        return template
 | 
			
		||||
							
								
								
									
										35
									
								
								tools/cwrap/plugins/BoolOption.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								tools/cwrap/plugins/BoolOption.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
if sys.version_info[0] == 3:
 | 
			
		||||
    string_type = str
 | 
			
		||||
else:
 | 
			
		||||
    string_type = basestring
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BoolOption(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    UNPACK_TEMPLATE = Template('$arg == Py_True ? $if_true : $if_false')
 | 
			
		||||
 | 
			
		||||
    def is_bool_option(self, arg):
 | 
			
		||||
        return arg['type'] == 'bool' and 'if_true' in arg and 'if_false' in arg
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            for option in declaration['options']:
 | 
			
		||||
                for arg in option['arguments']:
 | 
			
		||||
                    if self.is_bool_option(arg):
 | 
			
		||||
                        arg['is_bool_option'] = True
 | 
			
		||||
                        if isinstance(arg['if_true'], string_type):
 | 
			
		||||
                            arg['type'] = 'const char*'
 | 
			
		||||
        return declarations
 | 
			
		||||
 | 
			
		||||
    def get_type_check(self, arg, option):
 | 
			
		||||
        if arg.get('is_bool_option', False):
 | 
			
		||||
            return Template('PyBool_Check($arg)')
 | 
			
		||||
 | 
			
		||||
    def get_type_unpack(self, arg, option):
 | 
			
		||||
        if arg.get('is_bool_option', False):
 | 
			
		||||
            return Template(self.UNPACK_TEMPLATE.safe_substitute(
 | 
			
		||||
                if_true=arg['if_true'], if_false=arg['if_false']))
 | 
			
		||||
							
								
								
									
										318
									
								
								tools/cwrap/plugins/Broadcast.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								tools/cwrap/plugins/Broadcast.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,318 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
# Arguments to the Broadcast Plugin:
 | 
			
		||||
# broadcast: args_to_broadcast_against [inplace] [fallback]
 | 
			
		||||
# [args_to_broadcast_against]: either a single argument (e.g. "arg1") or a comma-seperated
 | 
			
		||||
#                              list of two arguments (e.g. "tensor1,tensor2") indicating
 | 
			
		||||
#                              arguments to broadcast specified argument (usually "self") against
 | 
			
		||||
# [inplace] will generate code for in-place function, which doesn't allow the in-place
 | 
			
		||||
#           argument to be broadcast
 | 
			
		||||
# [fallback] if tensors aren't broadcastable, preserves "element number" pointwise behavior,
 | 
			
		||||
#            where only number of elements need to match, and tensors are viewed as 1-dimensional.
 | 
			
		||||
# [dims] specify if the tensors shouldn't be broadcast to a specific tensor or tensors, but a combination
 | 
			
		||||
#        of individual dimension sizes of a set of tensors.  For example: addbmm(C,A,B) a.k.a. [C + A @ B]
 | 
			
		||||
#        broadcasts C to the first dimension of A and the second dimension of B.  Each dimension is specified as
 | 
			
		||||
#        [arg].dim[#] and dimensions are comma-separated.  So, to specify that the tensor should be
 | 
			
		||||
#        broadcast to 3-dimensions with sizes:
 | 
			
		||||
#        tensor0->size[0] x tensor1->size[1] x tensor2->size[2]
 | 
			
		||||
#        you would write:
 | 
			
		||||
#        dims:tensor0.dim0,tensor1.dim1,tensor2.dim2
 | 
			
		||||
# [types] if the tensors should be of different types than THTensor, specify as X where
 | 
			
		||||
#         the actual type to use is THXTensor (i.e. Byte for THByteTensor).  If the type
 | 
			
		||||
#         should be THTensor, use 'Real'
 | 
			
		||||
 | 
			
		||||
# For out of place:
 | 
			
		||||
# Two args: expand the two args together
 | 
			
		||||
# Three args (fused kernels): (e.g. addcmul) expand all three args together
 | 
			
		||||
# Sketch of proof that this is the same:
 | 
			
		||||
# consider addcmul, under expansion we want: a + (b * c) = (a + b * c) [all expanded together]
 | 
			
		||||
# Let e(i, j) be the expansion of i with j, e(i, j, k) be the expansion of i with j,k
 | 
			
		||||
#
 | 
			
		||||
# Then a + (b * c) = e(a, e(b,c) * e(c,b)) + e(e(b,c)   * e(c,b), a)
 | 
			
		||||
#                  = e(a, e(b,c))          + e(e(b,c)   * e(c,b), a)    (only size matters for second param)
 | 
			
		||||
#                  = e(a,b,c)              + e(e(b,c)   * e(c,b), a)    (by associativity of max in expand)
 | 
			
		||||
#                  = e(a,b,c)              + e(b,c,a)   * e(c,b,a)      (see L1)
 | 
			
		||||
# which is a + b * c all expanded together
 | 
			
		||||
#
 | 
			
		||||
# L1: Show e(i * j, a) = e(i,a) * e(j,a) where i,j have same size
 | 
			
		||||
# Consider any index _{ s_0, ..., s_n}
 | 
			
		||||
# e(i * j, a) = (i*j)_{f(s_0), ...,f(s_n)} where f is the expansion of that dimension with a
 | 
			
		||||
#             = i_{f(s_0), ..., f(s_n)} * j_{f(s_0), ..., f(s_n)} by definition of pointwise operator
 | 
			
		||||
#             = e(i,a) * e(j,a)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Broadcast(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    # Save and restore passed in arguments in case later plugins use
 | 
			
		||||
    POST_TEMPLATE = Template(
 | 
			
		||||
        """${arg_op_other} = ${arg_op_other}_save;\n""")
 | 
			
		||||
 | 
			
		||||
    def getPreArgStringTemplate(self, type=None):
 | 
			
		||||
        if type is None:
 | 
			
		||||
            ret = """THTensor *${arg_op_other}_save = ${arg_op_other};
 | 
			
		||||
                     THTensorPtr ${arg_op_other}_guard(THTensor_(new)(LIBRARY_STATE_NOARGS));\n"""
 | 
			
		||||
        else:
 | 
			
		||||
            cpu_t = "TH" + type + "Tensor"
 | 
			
		||||
            gpu_t = "THCuda" + type + "Tensor"
 | 
			
		||||
            ret = ("#if !IS_CUDA\n" +
 | 
			
		||||
                   cpu_t + " *${arg_op_other}_save = ${arg_op_other};\n" +
 | 
			
		||||
                   cpu_t + "Ptr ${arg_op_other}_guard(" + cpu_t + "_new(LIBRARY_STATE_NOARGS));\n" +
 | 
			
		||||
                   "#else\n" +
 | 
			
		||||
                   gpu_t + " *${arg_op_other}_save = ${arg_op_other};\n" +
 | 
			
		||||
                   "THPPointer<" + gpu_t + "> ${arg_op_other}_guard(\n" + gpu_t + "_new(LIBRARY_STATE_NOARGS));\n" +
 | 
			
		||||
                   "#endif\n")
 | 
			
		||||
        return Template(ret)
 | 
			
		||||
 | 
			
		||||
    def getExpandTemplate(self, expand_call, success_code, raise_errors):
 | 
			
		||||
        if not raise_errors:
 | 
			
		||||
            return Template(
 | 
			
		||||
                "bool expand_success = false;\n" +
 | 
			
		||||
                "try {\n" +
 | 
			
		||||
                expand_call +
 | 
			
		||||
                "\nexpand_success = true;\n" +
 | 
			
		||||
                "}\n"
 | 
			
		||||
                "catch (std::exception &e) {}\n" +
 | 
			
		||||
                "if(expand_success) {\n" +
 | 
			
		||||
                success_code +
 | 
			
		||||
                "\n}\n")
 | 
			
		||||
        else:
 | 
			
		||||
            return Template(
 | 
			
		||||
                expand_call + "\n" +
 | 
			
		||||
                success_code + "\n")
 | 
			
		||||
 | 
			
		||||
    def getOutPlacePreExpand2Template(self, raise_errors):
 | 
			
		||||
        expand_code = """expand_outplace2(LIBRARY_STATE ${arg_op_a}_guard.get(), ${arg_op_other}_guard.get(),
 | 
			
		||||
                                          ${arg_op_a}, ${arg_op_other},
 | 
			
		||||
                                          \"${op_a}\", \"${op_other}\", !${raise_errors});"""
 | 
			
		||||
        success_code = """${arg_op_a} = ${arg_op_a}_guard.get();
 | 
			
		||||
                          ${arg_op_other} = ${arg_op_other}_guard.get();"""
 | 
			
		||||
        return self.getExpandTemplate(expand_code, success_code, raise_errors)
 | 
			
		||||
 | 
			
		||||
    def getOutPlacePreExpand3Template(self, raise_errors):
 | 
			
		||||
        expand_code = """expand_outplace3(LIBRARY_STATE ${arg_op_a}_guard.get(),
 | 
			
		||||
                                          ${arg_op_other1}_guard.get(), ${arg_op_other2}_guard.get(),
 | 
			
		||||
                                          ${arg_op_a}, ${arg_op_other1}, ${arg_op_other2},
 | 
			
		||||
                                          \"${op_a}\", \"${op_other1}\", \"${op_other2}\", !${raise_errors});"""
 | 
			
		||||
        success_code = """${arg_op_a} = ${arg_op_a}_guard.get();
 | 
			
		||||
                          ${arg_op_other1} = ${arg_op_other1}_guard.get();
 | 
			
		||||
                          ${arg_op_other2} = ${arg_op_other2}_guard.get();"""
 | 
			
		||||
        return self.getExpandTemplate(expand_code, success_code, raise_errors)
 | 
			
		||||
 | 
			
		||||
    OUT_PLACE_PRE_EXPAND_PRE_DIM_TEMPLATE = Template(
 | 
			
		||||
        """if(THTensor_(nDimension)(LIBRARY_STATE ${arg_op_dim}) <= ${arg_op_dim_value}) {
 | 
			
		||||
             THError("Argument %s requires at least %d dimensions, but only has %d",
 | 
			
		||||
                     "${op_dim}", ${arg_op_dim_value} + 1, THTensor_(nDimension)(LIBRARY_STATE ${arg_op_dim}));
 | 
			
		||||
           }
 | 
			
		||||
           long ${arg_op_a}_dim${idx}_size = THTensor_(size)(LIBRARY_STATE ${arg_op_dim}, ${arg_op_dim_value});\n""")
 | 
			
		||||
 | 
			
		||||
    OUT_PLACE_PRE_EXPAND1_DIM_TEMPLATE = Template(
 | 
			
		||||
        """THLongStoragePtr ${arg_op_a}_storage(THLongStorage_newWithSize1(${arg_op_a}_dim0_size));\n""")
 | 
			
		||||
 | 
			
		||||
    OUT_PLACE_PRE_EXPAND2_DIM_TEMPLATE = Template(
 | 
			
		||||
        """THLongStoragePtr ${arg_op_a}_storage(
 | 
			
		||||
               THLongStorage_newWithSize2(${arg_op_a}_dim0_size, ${arg_op_a}_dim1_size));\n""")
 | 
			
		||||
 | 
			
		||||
    OUT_PLACE_PRE_EXPAND3_DIM_TEMPLATE = Template(
 | 
			
		||||
        """THLongStoragePtr ${arg_op_a}_storage(
 | 
			
		||||
               THLongStorage_newWithSize3(${arg_op_a}_dim0_size, ${arg_op_a}_dim1_size, ${arg_op_a}_dim2_size));\n""")
 | 
			
		||||
 | 
			
		||||
    def getOutPlacePreExpandPostDimTemplate(self, raise_errors):
 | 
			
		||||
        expand_code = """expand(LIBRARY_STATE ${arg_op_a}_guard.get(), ${arg_op_a}, ${arg_op_a}_storage);"""
 | 
			
		||||
        success_code = """${arg_op_a} = ${arg_op_a}_guard.get();"""
 | 
			
		||||
        return self.getExpandTemplate(expand_code, success_code, raise_errors)
 | 
			
		||||
 | 
			
		||||
    OUT_PLACE_PRE_TEMPLATE = Template(
 | 
			
		||||
        """${code_arg_op_a}${code_arg_op_other1}${code_arg_op_other2}
 | 
			
		||||
           ${expand_code}""")
 | 
			
		||||
 | 
			
		||||
    def getInPlacePreExpand1Template(self, raise_errors):
 | 
			
		||||
        expand_code = """expand_inplace1(LIBRARY_STATE ${arg_op_other}_guard.get(), ${arg_op_other}, ${arg_op_a},
 | 
			
		||||
                                         \"${op_other}\", \"${op_a}\", !${raise_errors});"""
 | 
			
		||||
        success_code = """${arg_op_other} = ${arg_op_other}_guard.get();"""
 | 
			
		||||
        return self.getExpandTemplate(expand_code, success_code, raise_errors)
 | 
			
		||||
 | 
			
		||||
    def getInPlacePreExpand2Template(self, raise_errors):
 | 
			
		||||
        expand_code = """expand_inplace2(LIBRARY_STATE ${arg_op_other1}_guard.get(), ${arg_op_other2}_guard.get(),
 | 
			
		||||
                                         ${arg_op_other1}, ${arg_op_other2}, ${arg_op_a},
 | 
			
		||||
                                         \"${op_other1}\", \"${op_other2}\", \"${op_a}\", !${raise_errors});"""
 | 
			
		||||
        success_code = """${arg_op_other1} = ${arg_op_other1}_guard.get();
 | 
			
		||||
                          ${arg_op_other2} = ${arg_op_other2}_guard.get();"""
 | 
			
		||||
        return self.getExpandTemplate(expand_code, success_code, raise_errors)
 | 
			
		||||
 | 
			
		||||
    IN_PLACE_PRE_TEMPLATE = Template(
 | 
			
		||||
        """${code_arg_op_other1}${code_arg_op_other2}
 | 
			
		||||
           ${expand_code}""")
 | 
			
		||||
 | 
			
		||||
    def initialize(self, cwrap):
 | 
			
		||||
        self.cwrap = cwrap
 | 
			
		||||
 | 
			
		||||
    # Arguments:
 | 
			
		||||
    # [0]: name of tensor to broadcast with (possibly two comma separated)
 | 
			
		||||
    # [1] inplace (optional).  In place operations only broadcast on second tensor argument
 | 
			
		||||
    # [2] fallback (optional).  Will fallback to applying to tensor of equal nElem if broadcast fails
 | 
			
		||||
    def process_option_code_template(self, template, option):
 | 
			
		||||
        new_code_pre = []
 | 
			
		||||
        new_code_post = []
 | 
			
		||||
        for _, arg in enumerate(option['arguments']):
 | 
			
		||||
            if 'broadcast' not in arg:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            params = arg.get('broadcast').split(" ")
 | 
			
		||||
            op_a = arg.get('assign_name', arg['name'])
 | 
			
		||||
            in_place = "inplace" in params
 | 
			
		||||
            raise_errors = "false" if "fallback" in params else "true"
 | 
			
		||||
 | 
			
		||||
            param_others = params[0].split(",")
 | 
			
		||||
            if len(param_others) > 2:
 | 
			
		||||
                raise ValueError('Broadcast only supports up to 2 secondary parameters')
 | 
			
		||||
            op_b = param_others[0]
 | 
			
		||||
            op_c = param_others[1] if len(param_others) == 2 else None
 | 
			
		||||
            arg_op_b = "arg_" + op_b
 | 
			
		||||
            arg_op_a = "arg_" + op_a
 | 
			
		||||
            arg_op_c = ("arg_" + op_c) if op_c else None
 | 
			
		||||
 | 
			
		||||
            dims_kvs = []
 | 
			
		||||
            for p in params:
 | 
			
		||||
                if p.startswith("dims:"):
 | 
			
		||||
                    assert(raise_errors == "true")
 | 
			
		||||
                    if len(dims_kvs) != 0:
 | 
			
		||||
                        raise ValueError("multiple specifications of dims")
 | 
			
		||||
                    dims = p[len("dims:"):].split(",")
 | 
			
		||||
                    for dim in dims:
 | 
			
		||||
                        batchdim = dim.split(".")
 | 
			
		||||
                        assert len(batchdim) == 2
 | 
			
		||||
                        assert batchdim[1].startswith("dim")
 | 
			
		||||
                        dim_val = batchdim[1][len("dim"):]
 | 
			
		||||
                        dims_kvs.append({"op": batchdim[0], "arg_op": "arg_" + batchdim[0], "val": dim_val})
 | 
			
		||||
 | 
			
		||||
            assert len(dims_kvs) <= 3
 | 
			
		||||
            for p in params[1:]:
 | 
			
		||||
                if p != "inplace" and p != "fallback" and not p.startswith("dims:") and not p.startswith("types:"):
 | 
			
		||||
                    raise ValueError("invalid parameter {}".format(p))
 | 
			
		||||
 | 
			
		||||
            type_op_b = None
 | 
			
		||||
            type_op_c = None
 | 
			
		||||
            for p in params:
 | 
			
		||||
                if p.startswith("types:"):
 | 
			
		||||
                    if not in_place and len(dims_kvs) > 0:
 | 
			
		||||
                        raise ValueError("type specification not supported yet for out-of-place functions "
 | 
			
		||||
                                         "that specify explicit dimensions")
 | 
			
		||||
                    types = p[len("types:"):].split(",")
 | 
			
		||||
                    assert(len(types) == (2 if op_c else 1))
 | 
			
		||||
                    type_op_b = None if types[0] == "Real" else types[0]
 | 
			
		||||
                    if op_c:
 | 
			
		||||
                        type_op_c = None if types[1] == "Real" else types[1]
 | 
			
		||||
 | 
			
		||||
            op_b_mapping = {
 | 
			
		||||
                "op_a": op_a,
 | 
			
		||||
                "op_other": op_b,
 | 
			
		||||
                "arg_op_a": arg_op_a,
 | 
			
		||||
                "arg_op_other": arg_op_b,
 | 
			
		||||
                "raise_errors": raise_errors
 | 
			
		||||
            }
 | 
			
		||||
            op_c_mapping = {
 | 
			
		||||
                "op_a": op_a,
 | 
			
		||||
                "op_other": op_c,
 | 
			
		||||
                "arg_op_a": arg_op_a,
 | 
			
		||||
                "arg_op_other": arg_op_c,
 | 
			
		||||
                "raise_errors": raise_errors
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if in_place:
 | 
			
		||||
                code_arg_op_other1 = self.getPreArgStringTemplate(type=type_op_b).substitute(op_b_mapping)
 | 
			
		||||
                code_arg_op_other2 = (
 | 
			
		||||
                    self.getPreArgStringTemplate(type=type_op_c).substitute(op_c_mapping) if op_c else "")
 | 
			
		||||
 | 
			
		||||
                if op_c:
 | 
			
		||||
                    expand_code = self.getInPlacePreExpand2Template(raise_errors == "true").substitute(
 | 
			
		||||
                        op_b_mapping,
 | 
			
		||||
                        op_other1=op_b,
 | 
			
		||||
                        op_other2=op_c,
 | 
			
		||||
                        arg_op_other1=arg_op_b,
 | 
			
		||||
                        arg_op_other2=arg_op_c)
 | 
			
		||||
                else:
 | 
			
		||||
                    expand_code = self.getInPlacePreExpand1Template(raise_errors == "true").substitute(op_b_mapping)
 | 
			
		||||
 | 
			
		||||
                new_code_pre.append(self.IN_PLACE_PRE_TEMPLATE.substitute(
 | 
			
		||||
                    arg_op_a=arg_op_a,
 | 
			
		||||
                    code_arg_op_other1=code_arg_op_other1,
 | 
			
		||||
                    code_arg_op_other2=code_arg_op_other2,
 | 
			
		||||
                    expand_code=expand_code,
 | 
			
		||||
                    raise_errors=raise_errors))
 | 
			
		||||
                new_code_pre.append("")
 | 
			
		||||
 | 
			
		||||
                post_code = self.POST_TEMPLATE.substitute(op_b_mapping)
 | 
			
		||||
                if op_c:
 | 
			
		||||
                    post_code += self.POST_TEMPLATE.substitute(op_c_mapping)
 | 
			
		||||
 | 
			
		||||
                new_code_post.append(post_code)
 | 
			
		||||
                new_code_post.append("")
 | 
			
		||||
            else:
 | 
			
		||||
                if len(dims_kvs) != 0:
 | 
			
		||||
                    code_arg_op_a = self.getPreArgStringTemplate().substitute(arg_op_other=arg_op_a)
 | 
			
		||||
                    code_arg_op_other1 = ""
 | 
			
		||||
                    code_arg_op_other2 = ""
 | 
			
		||||
                    expand_code = ""
 | 
			
		||||
                    for idx, kv in enumerate(dims_kvs):
 | 
			
		||||
                        expand_code += self.OUT_PLACE_PRE_EXPAND_PRE_DIM_TEMPLATE.substitute(
 | 
			
		||||
                            arg_op_a=arg_op_a,
 | 
			
		||||
                            op_dim=kv["op"],
 | 
			
		||||
                            arg_op_dim=kv["arg_op"],
 | 
			
		||||
                            arg_op_dim_value=kv["val"],
 | 
			
		||||
                            idx=idx)
 | 
			
		||||
 | 
			
		||||
                    if len(dims_kvs) == 1:
 | 
			
		||||
                        expand_code += self.OUT_PLACE_PRE_EXPAND1_DIM_TEMPLATE.substitute(
 | 
			
		||||
                            arg_op_a=arg_op_a,
 | 
			
		||||
                            arg_op_dim0=dims_kvs[0]["arg_op"])
 | 
			
		||||
                    elif len(dims_kvs) == 2:
 | 
			
		||||
                        expand_code += self.OUT_PLACE_PRE_EXPAND2_DIM_TEMPLATE.substitute(
 | 
			
		||||
                            arg_op_a=arg_op_a,
 | 
			
		||||
                            arg_op_dim0=dims_kvs[0]["arg_op"],
 | 
			
		||||
                            arg_op_dim1=dims_kvs[1]["arg_op"])
 | 
			
		||||
                    else:
 | 
			
		||||
                        expand_code += self.OUT_PLACE_PRE_EXPAND3_DIM_TEMPLATE.substitute(
 | 
			
		||||
                            arg_op_a=arg_op_a,
 | 
			
		||||
                            arg_op_dim0=dims_kvs[0]["arg_op"],
 | 
			
		||||
                            arg_op_dim1=dims_kvs[1]["arg_op"],
 | 
			
		||||
                            arg_op_dim2=dims_kvs[2]["arg_op"])
 | 
			
		||||
                    expand_code += self.getOutPlacePreExpandPostDimTemplate(raise_errors == "true").substitute(
 | 
			
		||||
                        arg_op_a=arg_op_a,
 | 
			
		||||
                        raise_errors=raise_errors)
 | 
			
		||||
                    post_code = self.POST_TEMPLATE.substitute(arg_op_other=arg_op_a)
 | 
			
		||||
 | 
			
		||||
                else:
 | 
			
		||||
                    code_arg_op_a = self.getPreArgStringTemplate().substitute(arg_op_other=arg_op_a)
 | 
			
		||||
                    code_arg_op_other1 = self.getPreArgStringTemplate(type=type_op_b).substitute(op_b_mapping)
 | 
			
		||||
                    code_arg_op_other2 = (self.getPreArgStringTemplate(type=type_op_c).substitute(op_c_mapping)
 | 
			
		||||
                                          if op_c else "")
 | 
			
		||||
 | 
			
		||||
                    if op_c:
 | 
			
		||||
                        expand_code = self.getOutPlacePreExpand3Template(raise_errors == "true").substitute(
 | 
			
		||||
                            op_b_mapping,
 | 
			
		||||
                            op_other1=op_b,
 | 
			
		||||
                            op_other2=op_c,
 | 
			
		||||
                            arg_op_other1=arg_op_b,
 | 
			
		||||
                            arg_op_other2=arg_op_c)
 | 
			
		||||
 | 
			
		||||
                    else:
 | 
			
		||||
                        expand_code = self.getOutPlacePreExpand2Template(
 | 
			
		||||
                            raise_errors == "true").substitute(op_b_mapping)
 | 
			
		||||
 | 
			
		||||
                    post_code = self.POST_TEMPLATE.substitute(arg_op_other=arg_op_a)
 | 
			
		||||
                    post_code += self.POST_TEMPLATE.substitute(op_b_mapping)
 | 
			
		||||
                    post_code += self.POST_TEMPLATE.substitute(op_c_mapping) if op_c else ""
 | 
			
		||||
 | 
			
		||||
                new_code_pre.append(self.OUT_PLACE_PRE_TEMPLATE.substitute(
 | 
			
		||||
                    code_arg_op_a=code_arg_op_a,
 | 
			
		||||
                    code_arg_op_other1=code_arg_op_other1,
 | 
			
		||||
                    code_arg_op_other2=code_arg_op_other2,
 | 
			
		||||
                    expand_code=expand_code))
 | 
			
		||||
                new_code_pre.append("")
 | 
			
		||||
 | 
			
		||||
                new_code_post.append(post_code)
 | 
			
		||||
                new_code_post.append("")
 | 
			
		||||
 | 
			
		||||
        template = new_code_pre + template + new_code_post
 | 
			
		||||
        return template
 | 
			
		||||
							
								
								
									
										21
									
								
								tools/cwrap/plugins/ConstantArguments.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/cwrap/plugins/ConstantArguments.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConstantArguments(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            for option in declaration['options']:
 | 
			
		||||
                for arg in option['arguments']:
 | 
			
		||||
                    if arg['type'] == 'CONSTANT':
 | 
			
		||||
                        arg['ignore_check'] = True
 | 
			
		||||
        return declarations
 | 
			
		||||
 | 
			
		||||
    def get_type_unpack(self, arg, option):
 | 
			
		||||
        if arg['type'] == 'CONSTANT':
 | 
			
		||||
            return Template('$arg')
 | 
			
		||||
 | 
			
		||||
    def get_arg_accessor(self, arg, option):
 | 
			
		||||
        if arg['type'] == 'CONSTANT':
 | 
			
		||||
            return arg['name']
 | 
			
		||||
							
								
								
									
										179
									
								
								tools/cwrap/plugins/CuDNNPlugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								tools/cwrap/plugins/CuDNNPlugin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,179 @@
 | 
			
		||||
from string import Template
 | 
			
		||||
import copy
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from itertools import product
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CuDNNPlugin(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    TYPE_UNPACK = {
 | 
			
		||||
        'THTensor*': Template('((THPVoidTensor*)$arg)->cdata'),
 | 
			
		||||
        'int': Template('THPUtils_unpackLong($arg)'),
 | 
			
		||||
        'std::vector<int>': Template('THPUtils_unpackIntTuple($arg)'),
 | 
			
		||||
        'cudnnDataType_t': Template('$arg'),
 | 
			
		||||
        'cudnnHandle_t': Template('$arg'),
 | 
			
		||||
        'Convolution*': Template('(Convolution*)THPWrapper_get($arg)'),
 | 
			
		||||
        'bool': Template('$arg == Py_True'),
 | 
			
		||||
        'double': Template('THPDoubleUtils_unpackReal($arg)'),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    INPUT_ARGUMENT_MAP = {
 | 
			
		||||
        'THTensor*': 'THVoidTensor*',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TYPE_CHECK = {
 | 
			
		||||
        'Convolution*': Template('THPWrapper_check($arg)'),
 | 
			
		||||
        'THTensor*': Template('(PyObject*)Py_TYPE($arg) == tensorClass'),
 | 
			
		||||
        'int': Template('THPUtils_checkLong($arg)'),
 | 
			
		||||
        'std::vector<int>': Template('THPUtils_checkIntTuple($arg)'),
 | 
			
		||||
        'bool': Template('PyBool_Check($arg)'),
 | 
			
		||||
        'double': Template('THPDoubleUtils_checkReal($arg)'),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RETURN_WRAPPER = {
 | 
			
		||||
        'Convolution*': Template('return THPWrapper_New($result, [](void* arg) { delete (Convolution*)arg; });'),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    METHODS_DECLARATION = Template("""
 | 
			
		||||
static PyMethodDef _THCUDNN_methods[] = {
 | 
			
		||||
$methods
 | 
			
		||||
  {NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyMethodDef* THCUDNN_methods()
 | 
			
		||||
{
 | 
			
		||||
  return _THCUDNN_methods;
 | 
			
		||||
}
 | 
			
		||||
""")
 | 
			
		||||
 | 
			
		||||
    WRAPPER_TEMPLATE = Template("""\
 | 
			
		||||
static PyObject * $name(PyObject *self, PyObject *args, PyObject *kwargs)
 | 
			
		||||
{
 | 
			
		||||
    HANDLE_TH_ERRORS
 | 
			
		||||
    int __tuplecount = args ? PyTuple_Size(args) : 0;
 | 
			
		||||
    int __dictcount = kwargs ? PyDict_Size(kwargs) : 0;
 | 
			
		||||
    int __argcount = __tuplecount + __dictcount;
 | 
			
		||||
    PyObject* tensorClass = getTensorClass(args);
 | 
			
		||||
    THCPAutoGPU __autogpu_guard = THCPAutoGPU(args);
 | 
			
		||||
 | 
			
		||||
    $options
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    THPUtils_invalidArguments(args, kwargs, "$readable_name", $num_options, $expected_args);
 | 
			
		||||
    return NULL;
 | 
			
		||||
    END_HANDLE_TH_ERRORS
 | 
			
		||||
}
 | 
			
		||||
""")
 | 
			
		||||
 | 
			
		||||
    RELEASE_ARG = Template("_${name}_guard.release();")
 | 
			
		||||
 | 
			
		||||
    TYPE_NAMES = {
 | 
			
		||||
        'THTensor*': '" THPTensorStr "',
 | 
			
		||||
        'long': 'int',
 | 
			
		||||
        'bool': 'bool',
 | 
			
		||||
        'int': 'int',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.declarations = []
 | 
			
		||||
 | 
			
		||||
    def get_type_unpack(self, arg, option):
 | 
			
		||||
        return self.TYPE_UNPACK.get(arg['type'], None)
 | 
			
		||||
 | 
			
		||||
    def get_type_check(self, arg, option):
 | 
			
		||||
        return self.TYPE_CHECK.get(arg['type'], None)
 | 
			
		||||
 | 
			
		||||
    def get_assign_args(self, arguments):
 | 
			
		||||
        assign_args = []
 | 
			
		||||
        for arg in arguments:
 | 
			
		||||
            arg = copy.copy(arg)
 | 
			
		||||
            new_type = self.INPUT_ARGUMENT_MAP.get(arg['type'])
 | 
			
		||||
            if new_type is not None:
 | 
			
		||||
                arg['type'] = new_type
 | 
			
		||||
            assign_args.append(arg)
 | 
			
		||||
        return assign_args
 | 
			
		||||
 | 
			
		||||
    def get_wrapper_template(self, declaration):
 | 
			
		||||
        arg_desc = []
 | 
			
		||||
        for option in declaration['options']:
 | 
			
		||||
            option_desc = [self.TYPE_NAMES.get(arg['type'], arg['type']) + ' ' + arg['name']
 | 
			
		||||
                           for arg in option['arguments']
 | 
			
		||||
                           if not arg.get('ignore_check', False)]
 | 
			
		||||
            # TODO: this should probably go to THPLongArgsPlugin
 | 
			
		||||
            if option_desc:
 | 
			
		||||
                arg_desc.append('({})'.format(', '.join(option_desc)))
 | 
			
		||||
            else:
 | 
			
		||||
                arg_desc.append('no arguments')
 | 
			
		||||
        arg_desc.sort(key=len)
 | 
			
		||||
        arg_desc = ['"' + desc + '"' for desc in arg_desc]
 | 
			
		||||
        arg_str = ', '.join(arg_desc)
 | 
			
		||||
        readable_name = declaration['python_name']
 | 
			
		||||
        return Template(self.WRAPPER_TEMPLATE.safe_substitute(
 | 
			
		||||
            readable_name=readable_name, num_options=len(arg_desc),
 | 
			
		||||
            expected_args=arg_str))
 | 
			
		||||
 | 
			
		||||
    def get_return_wrapper(self, option):
 | 
			
		||||
        return self.RETURN_WRAPPER.get(option['return'], None)
 | 
			
		||||
 | 
			
		||||
    def get_arg_accessor(self, arg, option):
 | 
			
		||||
        name = arg['name']
 | 
			
		||||
        if name == 'self':
 | 
			
		||||
            return 'self'
 | 
			
		||||
        elif name == 'dataType':
 | 
			
		||||
            return 'getCudnnDataType(tensorClass)'
 | 
			
		||||
        elif name == 'handle':
 | 
			
		||||
            return 'getCudnnHandle()'
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            declaration.setdefault('python_name', '_{}'.format(declaration['name']))
 | 
			
		||||
            declaration['name'] = 'THCUDNN_{}'.format(declaration['name'])
 | 
			
		||||
            self.declarations.append(declaration)
 | 
			
		||||
            for option in declaration['options']:
 | 
			
		||||
                for arg in option['arguments']:
 | 
			
		||||
                    if arg['name'] in ['self', 'state', 'dataType', 'handle']:
 | 
			
		||||
                        arg['ignore_check'] = True
 | 
			
		||||
            declaration['options'] = self.filter_unique_options(declaration['options'])
 | 
			
		||||
        return [d for d in declarations if not d.get('only_register', False)]
 | 
			
		||||
 | 
			
		||||
    def filter_unique_options(self, options):
 | 
			
		||||
        def signature(option):
 | 
			
		||||
            return '#'.join(arg['type'] for arg in option['arguments']
 | 
			
		||||
                            if 'ignore_check' not in arg or not arg['ignore_check'])
 | 
			
		||||
        seen_signatures = set()
 | 
			
		||||
        unique = []
 | 
			
		||||
        for option in options:
 | 
			
		||||
            sig = signature(option)
 | 
			
		||||
            if sig not in seen_signatures:
 | 
			
		||||
                unique.append(option)
 | 
			
		||||
                seen_signatures.add(sig)
 | 
			
		||||
        return unique
 | 
			
		||||
 | 
			
		||||
    def preprocessor_guard(self, code, condition):
 | 
			
		||||
        return '#if ' + condition + '\n' + code + '#endif\n'
 | 
			
		||||
 | 
			
		||||
    def process_wrapper(self, code, declaration):
 | 
			
		||||
        if 'defined_if' in declaration:
 | 
			
		||||
            return self.preprocessor_guard(code, declaration['defined_if'])
 | 
			
		||||
        return code
 | 
			
		||||
 | 
			
		||||
    def process_all_call_arg(self, code, option):
 | 
			
		||||
        return 'state, ' + code
 | 
			
		||||
 | 
			
		||||
    def declare_methods(self):
 | 
			
		||||
        methods = ''
 | 
			
		||||
        for declaration in self.declarations:
 | 
			
		||||
            extra_flags = ' | ' + declaration.get('method_flags') if 'method_flags' in declaration else ''
 | 
			
		||||
            if not declaration.get('only_register'):
 | 
			
		||||
                extra_flags += ' | METH_KEYWORDS'
 | 
			
		||||
            entry = Template('  {"$python_name", (PyCFunction)$name, METH_VARARGS$extra_flags, NULL},\n').substitute(
 | 
			
		||||
                python_name=declaration['python_name'], name=declaration['name'], extra_flags=extra_flags
 | 
			
		||||
            )
 | 
			
		||||
            if 'defined_if' in declaration:
 | 
			
		||||
                entry = self.preprocessor_guard(entry, declaration['defined_if'])
 | 
			
		||||
            methods += entry
 | 
			
		||||
        return self.METHODS_DECLARATION.substitute(methods=methods)
 | 
			
		||||
 | 
			
		||||
    def process_full_file(self, code):
 | 
			
		||||
        return code + self.declare_methods()
 | 
			
		||||
							
								
								
									
										31
									
								
								tools/cwrap/plugins/GILRelease.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tools/cwrap/plugins/GILRelease.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GILRelease(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    OPTION_START = [
 | 
			
		||||
        'PyThreadState *_save = NULL;',
 | 
			
		||||
        'try {',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    BEFORE_CALL = 'Py_UNBLOCK_THREADS;'
 | 
			
		||||
 | 
			
		||||
    AFTER_CALL = 'Py_BLOCK_THREADS;'
 | 
			
		||||
 | 
			
		||||
    OPTION_END = [
 | 
			
		||||
        '} catch (...) {',
 | 
			
		||||
        'if (_save) {',
 | 
			
		||||
        'Py_BLOCK_THREADS;',
 | 
			
		||||
        '}',
 | 
			
		||||
        'throw;',
 | 
			
		||||
        '}',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def process_option_code_template(self, template, option):
 | 
			
		||||
        if option.get('with_gil', False):
 | 
			
		||||
            return template
 | 
			
		||||
        call_idx = template.index('$call')
 | 
			
		||||
        template.insert(call_idx, self.BEFORE_CALL)
 | 
			
		||||
        template.insert(call_idx + 2, self.AFTER_CALL)
 | 
			
		||||
        return self.OPTION_START + template + self.OPTION_END
 | 
			
		||||
							
								
								
									
										223
									
								
								tools/cwrap/plugins/GenericNN.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								tools/cwrap/plugins/GenericNN.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,223 @@
 | 
			
		||||
import copy
 | 
			
		||||
from string import Template
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GenericNN(CWrapPlugin):
 | 
			
		||||
    INPUT_TYPE_CHECK = Template("checkTypes(is_cuda, $type, $tensor_args);")
 | 
			
		||||
 | 
			
		||||
    HEADER_TEMPLATE = Template("void $name($args);")
 | 
			
		||||
 | 
			
		||||
    WRAPPER_TEMPLATE = Template("""\
 | 
			
		||||
void $name($args)
 | 
			
		||||
{
 | 
			
		||||
  bool is_cuda = $input->isCuda();
 | 
			
		||||
  auto type = $input->type();
 | 
			
		||||
  $type_check
 | 
			
		||||
  $options
 | 
			
		||||
  } else {
 | 
			
		||||
    throw std::runtime_error("invalid arguments");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
""")
 | 
			
		||||
 | 
			
		||||
    THNN_TEMPLATE = Template("""\
 | 
			
		||||
    if (type == thpp::Type::FLOAT) {
 | 
			
		||||
        THNN_Float$name(
 | 
			
		||||
            NULL,
 | 
			
		||||
            $float_args);
 | 
			
		||||
    } else if (type == thpp::Type::DOUBLE) {
 | 
			
		||||
        THNN_Double$name(
 | 
			
		||||
            NULL,
 | 
			
		||||
            $double_args);
 | 
			
		||||
    } else {
 | 
			
		||||
        throw std::runtime_error("unsupported tensor type");
 | 
			
		||||
    }""")
 | 
			
		||||
 | 
			
		||||
    THCUNN_TEMPLATE = Template("""\
 | 
			
		||||
#ifdef WITH_CUDA
 | 
			
		||||
    if (type == thpp::Type::FLOAT) {
 | 
			
		||||
        THNN_Cuda$name(
 | 
			
		||||
            state,
 | 
			
		||||
            $float_args);
 | 
			
		||||
    } else if (type == thpp::Type::DOUBLE) {
 | 
			
		||||
        THNN_CudaDouble$name(
 | 
			
		||||
            state,
 | 
			
		||||
            $double_args);
 | 
			
		||||
    } else if (type == thpp::Type::HALF) {
 | 
			
		||||
        THNN_CudaHalf$name(
 | 
			
		||||
            state,
 | 
			
		||||
            $half_args);
 | 
			
		||||
    } else {
 | 
			
		||||
        throw std::runtime_error("unsupported tensor type");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
""")
 | 
			
		||||
 | 
			
		||||
    INDEX_TENSOR_TYPES = {'THIndexTensor*', 'THCIndexTensor*'}
 | 
			
		||||
 | 
			
		||||
    REAL_TENSOR_TYPES = {'THTensor*', 'THCTensor*'}
 | 
			
		||||
 | 
			
		||||
    INPUT_ARGUMENT_MAP = {
 | 
			
		||||
        'THNNState*': 'void*',
 | 
			
		||||
        'THCState*': 'void*',
 | 
			
		||||
        'THTensor*': 'thpp::Tensor*',
 | 
			
		||||
        'THCTensor*': 'thpp::Tensor*',
 | 
			
		||||
        'THIndexTensor*': 'thpp::Tensor*',
 | 
			
		||||
        'THCIndexTensor*': 'thpp::Tensor*',
 | 
			
		||||
        'THIndex_t': 'long',
 | 
			
		||||
        'accreal': 'double',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, header=False):
 | 
			
		||||
        self.header = header
 | 
			
		||||
        self.declarations = []
 | 
			
		||||
 | 
			
		||||
    def process_full_file(self, base_wrapper):
 | 
			
		||||
        if self.header:
 | 
			
		||||
            wrapper = '#pragma once\n\n'
 | 
			
		||||
            wrapper += '#include <THPP/Tensor.hpp>\n\n'
 | 
			
		||||
        else:
 | 
			
		||||
            wrapper = '#include "THNN_generic.h"\n'
 | 
			
		||||
            wrapper = '#include "THNN_generic.inc.h"\n\n'
 | 
			
		||||
        wrapper += 'namespace torch { namespace nn {\n\n'
 | 
			
		||||
        wrapper += base_wrapper
 | 
			
		||||
        wrapper += '}} // namespace torch::nn\n'
 | 
			
		||||
        return wrapper
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            base_args = declaration['options'][0]['arguments']
 | 
			
		||||
            for option in declaration['options']:
 | 
			
		||||
                for idx, arg in enumerate(option['arguments']):
 | 
			
		||||
                    arg['assign_name'] = base_args[idx]['name']
 | 
			
		||||
                    arg['assign_type'] = base_args[idx]['type']
 | 
			
		||||
                    if idx != 1:
 | 
			
		||||
                        arg['ignore_check'] = True
 | 
			
		||||
        return declarations
 | 
			
		||||
 | 
			
		||||
    def get_arg_accessor(self, arg, option):
 | 
			
		||||
        return self.get_type_unpack(arg, option)
 | 
			
		||||
 | 
			
		||||
    def process_pre_arg_assign(self, pre_arg_assign, option):
 | 
			
		||||
        if option['backend'] == 'cunn':
 | 
			
		||||
            # Enclose arg_assign with CUDA guard
 | 
			
		||||
            pre_arg_assign.append('#ifdef WITH_CUDA')
 | 
			
		||||
        return pre_arg_assign
 | 
			
		||||
 | 
			
		||||
    def process_option_code_template(self, template, option):
 | 
			
		||||
        template = []
 | 
			
		||||
        if option['backend'] == 'cunn':
 | 
			
		||||
            template.append('#endif')
 | 
			
		||||
 | 
			
		||||
        def base_cast(arg, CReal, real):
 | 
			
		||||
            name = 'arg_' + arg['assign_name']
 | 
			
		||||
            type = arg['type']
 | 
			
		||||
            if type in self.REAL_TENSOR_TYPES:
 | 
			
		||||
                return ('(TH{CReal}Tensor*){name}->cdata()'
 | 
			
		||||
                        .format(CReal=CReal, name=name))
 | 
			
		||||
            elif type in self.INDEX_TENSOR_TYPES:
 | 
			
		||||
                return '({type}){name}->cdata()'.format(type=type, name=name)
 | 
			
		||||
            elif type == 'THCState*':
 | 
			
		||||
                return '({}){}'.format(type, name)
 | 
			
		||||
            elif type == 'real':
 | 
			
		||||
                if real == 'half':
 | 
			
		||||
                    return 'THC_float2half({})'.format(name)
 | 
			
		||||
                return '({real}){name}'.format(real=real, name=name)
 | 
			
		||||
            return name
 | 
			
		||||
 | 
			
		||||
        def cast(arg, CReal, real):
 | 
			
		||||
            expr = base_cast(arg, CReal, real)
 | 
			
		||||
            if arg.get('optional', False):
 | 
			
		||||
                name = 'arg_' + arg['assign_name']
 | 
			
		||||
                return '{name} ? {expr} : NULL'.format(name=name, expr=expr)
 | 
			
		||||
            return expr
 | 
			
		||||
 | 
			
		||||
        if option['backend'] == 'nn':
 | 
			
		||||
            float_args = []
 | 
			
		||||
            double_args = []
 | 
			
		||||
            for idx, arg in enumerate(option['arguments']):
 | 
			
		||||
                float_args.append(cast(arg, 'Float', 'float'))
 | 
			
		||||
                double_args.append(cast(arg, 'Double', 'double'))
 | 
			
		||||
 | 
			
		||||
            code = self.THNN_TEMPLATE.substitute(
 | 
			
		||||
                name=option['cname'],
 | 
			
		||||
                float_args=',\n'.join(float_args),
 | 
			
		||||
                double_args=',\n'.join(double_args))
 | 
			
		||||
            template.append(code)
 | 
			
		||||
 | 
			
		||||
        elif option['backend'] == 'cunn':
 | 
			
		||||
            float_args = []
 | 
			
		||||
            double_args = []
 | 
			
		||||
            half_args = []
 | 
			
		||||
            for idx, arg in enumerate(option['arguments']):
 | 
			
		||||
                float_args.append(cast(arg, 'Cuda', 'float'))
 | 
			
		||||
                double_args.append(cast(arg, 'CudaDouble', 'double'))
 | 
			
		||||
                half_args.append(cast(arg, 'CudaHalf', 'half'))
 | 
			
		||||
 | 
			
		||||
            code = self.THCUNN_TEMPLATE.substitute(
 | 
			
		||||
                name=option['cname'],
 | 
			
		||||
                float_args=',\n'.join(float_args),
 | 
			
		||||
                double_args=',\n'.join(double_args),
 | 
			
		||||
                half_args=',\n'.join(half_args))
 | 
			
		||||
            template.append(code)
 | 
			
		||||
 | 
			
		||||
        template.append('')
 | 
			
		||||
        return template
 | 
			
		||||
 | 
			
		||||
    def get_type_unpack(self, arg, option):
 | 
			
		||||
        return Template(arg.get('assign_name', arg['name']))
 | 
			
		||||
 | 
			
		||||
    def get_type_check(self, arg, option):
 | 
			
		||||
        if option['backend'] == 'cunn':
 | 
			
		||||
            return Template('is_cuda')
 | 
			
		||||
        else:
 | 
			
		||||
            return Template('!is_cuda')
 | 
			
		||||
 | 
			
		||||
    def get_assign_args(self, arguments):
 | 
			
		||||
        assign_args = []
 | 
			
		||||
        for arg in arguments:
 | 
			
		||||
            arg = copy.copy(arg)
 | 
			
		||||
            new_type = self.INPUT_ARGUMENT_MAP.get(arg['type'])
 | 
			
		||||
            if new_type is not None:
 | 
			
		||||
                arg['type'] = new_type
 | 
			
		||||
            assign_args.append(arg)
 | 
			
		||||
        return assign_args
 | 
			
		||||
 | 
			
		||||
    def get_wrapper_template(self, declaration):
 | 
			
		||||
        # get assign arguments string
 | 
			
		||||
        base_arguments = declaration['options'][0]['arguments']
 | 
			
		||||
        args = self.get_assign_args(base_arguments)
 | 
			
		||||
        arg_str = ', '.join([arg['type'] + ' ' + arg['name'] for arg in args])
 | 
			
		||||
 | 
			
		||||
        if self.header:
 | 
			
		||||
            return Template(self.HEADER_TEMPLATE.safe_substitute(args=arg_str))
 | 
			
		||||
 | 
			
		||||
        def get_checked_args(tensor_types):
 | 
			
		||||
            checked_args = []
 | 
			
		||||
            for arg in base_arguments:
 | 
			
		||||
                if arg['type'] in tensor_types:
 | 
			
		||||
                    name = arg.get('assign_name', arg['name'])
 | 
			
		||||
                    name_str = name
 | 
			
		||||
                    if arg.get('optional', False):
 | 
			
		||||
                        name_str = '?' + name_str
 | 
			
		||||
                    checked_args += ['"' + name_str + '"', name]
 | 
			
		||||
            checked_args += ['NULL']
 | 
			
		||||
            return checked_args
 | 
			
		||||
 | 
			
		||||
        real_args = get_checked_args(self.REAL_TENSOR_TYPES)
 | 
			
		||||
        long_args = get_checked_args(self.INDEX_TENSOR_TYPES)
 | 
			
		||||
 | 
			
		||||
        # check input types
 | 
			
		||||
        types_checks = []
 | 
			
		||||
        if len(real_args) > 1:
 | 
			
		||||
            types_checks.append(self.INPUT_TYPE_CHECK.substitute(
 | 
			
		||||
                type='type', tensor_args=', '.join(real_args)))
 | 
			
		||||
        if len(long_args) > 1:
 | 
			
		||||
            types_checks.append(self.INPUT_TYPE_CHECK.substitute(
 | 
			
		||||
                type='thpp::Type::LONG', tensor_args=', '.join(long_args)))
 | 
			
		||||
 | 
			
		||||
        return Template(self.WRAPPER_TEMPLATE.safe_substitute(
 | 
			
		||||
            input=args[0]['name'],
 | 
			
		||||
            args=arg_str,
 | 
			
		||||
            type_check='\n  '.join(types_checks)))
 | 
			
		||||
							
								
								
									
										69
									
								
								tools/cwrap/plugins/KwargsPlugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tools/cwrap/plugins/KwargsPlugin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class KwargsPlugin(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    ACCESSOR_TEMPLATE = Template('(__tuplecount > $idx ? PyTuple_GET_ITEM(args, $idx) : __kw_$name)')
 | 
			
		||||
    KWARG_ONLY_ACCESSOR_TEMPLATE = Template('__kw_$name')
 | 
			
		||||
    CHECK_TEMPLATE = Template('(__tuplecount > $idx || __kw_$name) && $code')
 | 
			
		||||
    KWARG_ONLY_CHECK_TEMPLATE = Template('__kw_$name && $code')
 | 
			
		||||
    WRAPPER_TEMPLATE = Template("""
 | 
			
		||||
    $declarations
 | 
			
		||||
    if (kwargs) {
 | 
			
		||||
      $lookups
 | 
			
		||||
    }
 | 
			
		||||
    """)
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        # We don't have access to declaration or options in get_arg_accessor
 | 
			
		||||
        # and process_single_check, so we have to push the flag down to
 | 
			
		||||
        # the args.
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            if declaration.get('no_kwargs'):
 | 
			
		||||
                for option in declaration['options']:
 | 
			
		||||
                    for arg in option['arguments']:
 | 
			
		||||
                        arg['no_kwargs'] = True
 | 
			
		||||
        # we need to use offsets for arg position in *arg if kwarg_only args
 | 
			
		||||
        # are not at the end
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            for option in declaration['options']:
 | 
			
		||||
                offset = 0
 | 
			
		||||
                for arg in option['arguments']:
 | 
			
		||||
                    if arg.get('kwarg_only'):
 | 
			
		||||
                        arg['no_idx'] = True
 | 
			
		||||
        return declarations
 | 
			
		||||
 | 
			
		||||
    def get_arg_accessor(self, arg, option):
 | 
			
		||||
        if arg.get('no_kwargs'):
 | 
			
		||||
            return
 | 
			
		||||
        if arg.get('kwarg_only'):
 | 
			
		||||
            return self.KWARG_ONLY_ACCESSOR_TEMPLATE.substitute(name=arg['name'])
 | 
			
		||||
        return self.ACCESSOR_TEMPLATE.substitute(idx=arg['idx'], name=arg['name'])
 | 
			
		||||
 | 
			
		||||
    def process_single_check(self, code, arg, arg_accessor):
 | 
			
		||||
        if arg.get('no_kwargs'):
 | 
			
		||||
            return code
 | 
			
		||||
        if arg.get('kwarg_only'):
 | 
			
		||||
            return self.KWARG_ONLY_CHECK_TEMPLATE.substitute(name=arg['name'], code=code)
 | 
			
		||||
        return self.CHECK_TEMPLATE.substitute(idx=arg['idx'], name=arg['name'], code=code)
 | 
			
		||||
 | 
			
		||||
    def process_wrapper(self, code, declaration):
 | 
			
		||||
        if declaration.get('no_kwargs'):
 | 
			
		||||
            return code
 | 
			
		||||
        seen_args = set()
 | 
			
		||||
        args = []
 | 
			
		||||
        for option in declaration['options']:
 | 
			
		||||
            for arg in option['arguments']:
 | 
			
		||||
                name = arg['name']
 | 
			
		||||
                if (not arg.get('ignore_check') and
 | 
			
		||||
                        not arg.get('no_kwargs') and
 | 
			
		||||
                        name not in seen_args):
 | 
			
		||||
                    seen_args.add(name)
 | 
			
		||||
                    args.append(name)
 | 
			
		||||
        declarations = '\n    '.join(['PyObject *__kw_{} = NULL;'.format(a) for a in args])
 | 
			
		||||
        lookups = '\n      '.join(
 | 
			
		||||
            ['__kw_{name} = PyDict_GetItemString(kwargs, "{name}");'.format(name=a) for a in args])
 | 
			
		||||
        start_idx = code.find('{') + 1
 | 
			
		||||
        new_code = self.WRAPPER_TEMPLATE.substitute(declarations=declarations, lookups=lookups)
 | 
			
		||||
        return code[:start_idx] + new_code + code[start_idx:]
 | 
			
		||||
							
								
								
									
										14
									
								
								tools/cwrap/plugins/NullableArguments.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tools/cwrap/plugins/NullableArguments.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NullableArguments(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def process_single_check(self, code, arg, arg_accessor):
 | 
			
		||||
        if 'nullable' in arg and arg['nullable']:
 | 
			
		||||
            return '({} || {} == Py_None)'.format(code, arg_accessor)
 | 
			
		||||
        return code
 | 
			
		||||
 | 
			
		||||
    def process_single_unpack(self, code, arg, arg_accessor):
 | 
			
		||||
        if 'nullable' in arg and arg['nullable']:
 | 
			
		||||
            return '({} == Py_None ? NULL : {})'.format(arg_accessor, code)
 | 
			
		||||
        return code
 | 
			
		||||
							
								
								
									
										18
									
								
								tools/cwrap/plugins/OptionalArguments.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tools/cwrap/plugins/OptionalArguments.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
import os
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from itertools import product
 | 
			
		||||
from ...shared import cwrap_common
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OptionalArguments(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            cwrap_common.enumerate_options_due_to_default(
 | 
			
		||||
                declaration,
 | 
			
		||||
                allow_kwarg=True,
 | 
			
		||||
                type_to_signature={},
 | 
			
		||||
                remove_self=False)
 | 
			
		||||
 | 
			
		||||
        return declarations
 | 
			
		||||
							
								
								
									
										90
									
								
								tools/cwrap/plugins/ProcessorSpecificPlugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								tools/cwrap/plugins/ProcessorSpecificPlugin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
import yaml
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProcessorSpecificPlugin(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    def process_declarations(self, declarations):
 | 
			
		||||
        # In order to move Torch's random functions into the same cwrap
 | 
			
		||||
        # declaration, we need to be able to handle the fact that on the CPU
 | 
			
		||||
        # these functions take a generator argument, while on the GPU, they
 | 
			
		||||
        # do not. As such, we would like to split those declarations at cwrap
 | 
			
		||||
        # runtime into two separate declarations, one for the CPU (unchanged),
 | 
			
		||||
        # and one for the GPU (with the generator argument removed).
 | 
			
		||||
        #
 | 
			
		||||
        # For example, the declaration arguments:
 | 
			
		||||
        # arguments:
 | 
			
		||||
        #   - THTensor* self
 | 
			
		||||
        #   - arg: THGenerator* generator
 | 
			
		||||
        #     default: THPDefaultGenerator->cdata
 | 
			
		||||
        #     kwarg_only: True
 | 
			
		||||
        #
 | 
			
		||||
        # Would have the generator argument removed when generating for the GPU
 | 
			
		||||
        # backend.
 | 
			
		||||
 | 
			
		||||
        def arg_contains_generator(arg):
 | 
			
		||||
            return (arg['type'] == 'THGenerator*' or (arg.get('default', None)
 | 
			
		||||
                    is not None and 'THPDefaultGenerator' in
 | 
			
		||||
                    str(arg.get('default', ""))))
 | 
			
		||||
 | 
			
		||||
        def split_candidate(declaration):
 | 
			
		||||
            # First, check and see if it is a declaration for both CPU/GPU
 | 
			
		||||
            if all([proc in declaration['backends'] for
 | 
			
		||||
                    proc in ['CPU', 'CUDA']]):
 | 
			
		||||
                for option in declaration['options']:
 | 
			
		||||
                    for argument in option['arguments']:
 | 
			
		||||
                        if arg_contains_generator(argument):
 | 
			
		||||
                            return True
 | 
			
		||||
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        def can_we_handle_the_split(declaration):
 | 
			
		||||
            # hook into here if the split cannot happen for some reason
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        def generator_split(declaration):
 | 
			
		||||
            # the split must make two changes: 1. remove the generator argument
 | 
			
		||||
            # for the GPU, and 2. assign the correct backends/types to the
 | 
			
		||||
            # split declaration
 | 
			
		||||
            dec_cpu = declaration
 | 
			
		||||
            dec_gpu = deepcopy(declaration)
 | 
			
		||||
 | 
			
		||||
            # Remove GPU backend and types from dec_cpu
 | 
			
		||||
            dec_cpu['backends'].remove('CUDA')
 | 
			
		||||
            if dec_cpu.get('backend_type_pairs', False):
 | 
			
		||||
                dec_cpu['backend_type_pairs'] = (
 | 
			
		||||
                    [pair for pair in dec_cpu['backend_type_pairs'] if
 | 
			
		||||
                     pair[1] == 'CPU'])
 | 
			
		||||
            # also need to reach into options
 | 
			
		||||
            for option in dec_cpu['options']:
 | 
			
		||||
                option['backends'].remove('CUDA')
 | 
			
		||||
 | 
			
		||||
            # Remove CPU backend and types from dec_gpu
 | 
			
		||||
            dec_gpu['backends'].remove('CPU')
 | 
			
		||||
            if dec_gpu.get('backend_type_pairs', False):
 | 
			
		||||
                dec_gpu['backend_type_pairs'] = (
 | 
			
		||||
                    [pair for pair in dec_gpu['backend_type_pairs'] if
 | 
			
		||||
                     pair[1] == 'CUDA'])
 | 
			
		||||
            # also need to reach into options
 | 
			
		||||
            for option in dec_gpu['options']:
 | 
			
		||||
                option['backends'].remove('CPU')
 | 
			
		||||
 | 
			
		||||
            # Remove generator arguments from dec_gpu options
 | 
			
		||||
            for option in dec_gpu['options']:
 | 
			
		||||
                option['arguments'] = (
 | 
			
		||||
                    [arg for arg in option['arguments'] if
 | 
			
		||||
                     not arg_contains_generator(arg)])
 | 
			
		||||
 | 
			
		||||
            return [dec_cpu, dec_gpu]
 | 
			
		||||
 | 
			
		||||
        decs = []
 | 
			
		||||
        for declaration in declarations:
 | 
			
		||||
            if split_candidate(declaration):
 | 
			
		||||
                assert(can_we_handle_the_split(declaration))
 | 
			
		||||
                newdecs = generator_split(declaration)
 | 
			
		||||
                decs.extend(newdecs)
 | 
			
		||||
            else:
 | 
			
		||||
                decs.append(declaration)
 | 
			
		||||
 | 
			
		||||
        return decs
 | 
			
		||||
							
								
								
									
										21
									
								
								tools/cwrap/plugins/ReturnArguments.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/cwrap/plugins/ReturnArguments.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
from string import Template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ReturnArguments(CWrapPlugin):
 | 
			
		||||
    ARGUMENT_RETURN_TEMPLATE = Template("Py_INCREF($arg);\nreturn (PyObject*)($arg);")
 | 
			
		||||
    TUPLE_RETURN_TEMPLATE = Template("return PyTuple_Pack($num_args, $args);")
 | 
			
		||||
 | 
			
		||||
    def initialize(self, cwrap):
 | 
			
		||||
        self.cwrap = cwrap
 | 
			
		||||
 | 
			
		||||
    def get_return_wrapper(self, option):
 | 
			
		||||
        if option['return'].startswith('argument '):
 | 
			
		||||
            indices = list(map(int, option['return'][len('argument '):].split(',')))
 | 
			
		||||
            args = [option['arguments'][idx] for idx in indices]
 | 
			
		||||
            accessors = [self.cwrap.get_arg_accessor(arg, option) for arg in args]
 | 
			
		||||
            if len(args) == 1:
 | 
			
		||||
                return Template(self.ARGUMENT_RETURN_TEMPLATE.safe_substitute(arg=accessors[0]))
 | 
			
		||||
            else:
 | 
			
		||||
                return Template(self.TUPLE_RETURN_TEMPLATE.safe_substitute(num_args=len(args),
 | 
			
		||||
                                                                           args=', '.join(accessors)))
 | 
			
		||||
							
								
								
									
										151
									
								
								tools/cwrap/plugins/StandaloneExtension.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								tools/cwrap/plugins/StandaloneExtension.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,151 @@
 | 
			
		||||
import os
 | 
			
		||||
from string import Template
 | 
			
		||||
from . import CWrapPlugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MODULE_HEAD = """
 | 
			
		||||
#include <Python.h>
 | 
			
		||||
#include <exception>
 | 
			
		||||
 | 
			
		||||
#include "THP_API.h"
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
with open(os.path.join(os.path.dirname(__file__), 'templates', 'module_tail.cpp'), 'r') as f:
 | 
			
		||||
    MODULE_TAIL = Template(f.read())
 | 
			
		||||
 | 
			
		||||
REGISTER_METHOD_TEMPLATE = Template('  {"$name", (PyCFunction)$name, METH_VARARGS, NULL},\n')
 | 
			
		||||
 | 
			
		||||
MODULE_METHODS_TEMPLATE = Template("""
 | 
			
		||||
static PyMethodDef module_methods[] = {
 | 
			
		||||
$METHODS
 | 
			
		||||
  {NULL, NULL, 0, NULL}
 | 
			
		||||
};
 | 
			
		||||
""")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StandaloneExtension(CWrapPlugin):
 | 
			
		||||
 | 
			
		||||
    TYPE_UNPACK = {
 | 
			
		||||
        'THFloatTensor*': Template('THPFloatTensor_CData((THPFloatTensor*)$arg)'),
 | 
			
		||||
        'THDoubleTensor*': Template('THPDoubleTensor_CData((THPDoubleTensor*)$arg)'),
 | 
			
		||||
        'THLongTensor*': Template('THPLongTensor_CData((THPLongTensor*)$arg)'),
 | 
			
		||||
        'THIntTensor*': Template('THPIntTensor_CData((THPIntTensor*)$arg)'),
 | 
			
		||||
        'THCudaHalfTensor*': Template('THCPHalfTensor_CData((THCPHalfTensor*)$arg)'),
 | 
			
		||||
        'THCudaTensor*': Template('THCPFloatTensor_CData((THCPFloatTensor*)$arg)'),
 | 
			
		||||
        'THCudaDoubleTensor*': Template('THCPDoubleTensor_CData((THCPDoubleTensor*)$arg)'),
 | 
			
		||||
        'THCudaLongTensor*': Template('THCPLongTensor_CData((THCPLongTensor*)$arg)'),
 | 
			
		||||
        'half': Template('THPHalfUtils_unpackReal($arg)'),
 | 
			
		||||
        'float': Template('THPFloatUtils_unpackReal($arg)'),
 | 
			
		||||
        'double': Template('THPDoubleUtils_unpackReal($arg)'),
 | 
			
		||||
        'bool': Template('($arg == Py_True ? true : false)'),
 | 
			
		||||
        'int': Template('THPUtils_unpackLong($arg)'),
 | 
			
		||||
        'long': Template('THPUtils_unpackLong($arg)'),
 | 
			
		||||
        'void*': Template('(void*)THPUtils_unpackLong($arg)'),
 | 
			
		||||
        'THGenerator*': Template('THPGenerator_CData((THPGenerator*)$arg)'),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TYPE_CHECK = {
 | 
			
		||||
        'THDoubleTensor*': Template('(PyObject*)Py_TYPE($arg) == THPDoubleTensorClass'),
 | 
			
		||||
        'THFloatTensor*': Template('(PyObject*)Py_TYPE($arg) == THPFloatTensorClass'),
 | 
			
		||||
        'THLongTensor*': Template('(PyObject*)Py_TYPE($arg) == THPLongTensorClass'),
 | 
			
		||||
        'THIntTensor*': Template('(PyObject*)Py_TYPE($arg) == THPIntTensorClass'),
 | 
			
		||||
        'THCudaHalfTensor*': Template('THCPHalfTensor_Check($arg)'),
 | 
			
		||||
        'THCudaTensor*': Template('(PyObject*)Py_TYPE($arg) == THCPFloatTensorClass'),
 | 
			
		||||
        'THCudaDoubleTensor*': Template('THCPDoubleTensor_Check($arg)'),
 | 
			
		||||
        'THCudaLongTensor*': Template('(PyObject*)Py_TYPE($arg) == THCPLongTensorClass'),
 | 
			
		||||
        'half': Template('THPHalfUtils_checkReal($arg)'),
 | 
			
		||||
        'float': Template('THPFloatUtils_checkReal($arg)'),
 | 
			
		||||
        'double': Template('THPDoubleUtils_checkReal($arg)'),
 | 
			
		||||
        'bool': Template('PyBool_Check($arg)'),
 | 
			
		||||
        'int': Template('THPUtils_checkLong($arg)'),
 | 
			
		||||
        'long': Template('THPUtils_checkLong($arg)'),
 | 
			
		||||
        'void*': Template('THPUtils_checkLong($arg)'),
 | 
			
		||||
        'THGenerator*': Template('(PyObject*)Py_TYPE($arg) == THPGeneratorClass'),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WRAPPER_TEMPLATE = Template("""
 | 
			
		||||
PyObject * $name(PyObject *_unused, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
  HANDLE_TH_ERRORS
 | 
			
		||||
  int __argcount = args ? PyTuple_Size(args) : 0;
 | 
			
		||||
    $options
 | 
			
		||||
  } else {
 | 
			
		||||
    THPUtils_invalidArguments(args, NULL, "$name", 1, $expected_args);
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
  END_HANDLE_TH_ERRORS
 | 
			
		||||
}
 | 
			
		||||
    """)
 | 
			
		||||
 | 
			
		||||
    TYPE_NAMES = {
 | 
			
		||||
        'THGenerator*': 'Generator',
 | 
			
		||||
        'THCudaHalfTensor*': 'torch.cuda.HalfTensor',
 | 
			
		||||
        'THCudaTensor*': 'torch.cuda.FloatTensor',
 | 
			
		||||
        'THCudaDoubleTensor*': 'torch.cuda.DoubleTensor',
 | 
			
		||||
        'THCudaLongTensor*': 'torch.cuda.LongTensor',
 | 
			
		||||
        'THDoubleTensor*': 'torch.DoubleTensor',
 | 
			
		||||
        'THFloatTensor*': 'torch.FloatTensor',
 | 
			
		||||
        'THBoolTensor*': 'torch.ByteTensor',
 | 
			
		||||
        'THLongTensor*': 'torch.LongTensor',
 | 
			
		||||
        'THIndexTensor*': 'torch.LongTensor',
 | 
			
		||||
        'THIntTensor*': 'torch.IntTensor',
 | 
			
		||||
        'THLongStorage*': 'torch.LongStorage',
 | 
			
		||||
        'long': 'int',
 | 
			
		||||
        'int': 'int',
 | 
			
		||||
        'real': 'float',
 | 
			
		||||
        'half': 'float',
 | 
			
		||||
        'double': 'float',
 | 
			
		||||
        'float': 'float',
 | 
			
		||||
        'accreal': 'float',
 | 
			
		||||
        'bool': 'bool',
 | 
			
		||||
        'void*': 'int',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, module_name):
 | 
			
		||||
        self.module_name = module_name
 | 
			
		||||
        self.declarations = []
 | 
			
		||||
 | 
			
		||||
    def process_full_file(self, code):
 | 
			
		||||
        short_name = self.module_name.split('.')[-1]
 | 
			
		||||
        new_code = MODULE_HEAD
 | 
			
		||||
        new_code += code
 | 
			
		||||
        new_code += self.declare_module_methods()
 | 
			
		||||
        new_code += MODULE_TAIL.substitute(full_name=self.module_name, short_name=short_name)
 | 
			
		||||
        return new_code
 | 
			
		||||
 | 
			
		||||
    def process_wrapper(self, code, declaration):
 | 
			
		||||
        self.declarations.append(declaration)
 | 
			
		||||
        return code
 | 
			
		||||
 | 
			
		||||
    def declare_module_methods(self):
 | 
			
		||||
        module_methods = ''
 | 
			
		||||
        for declaration in self.declarations:
 | 
			
		||||
            module_methods += REGISTER_METHOD_TEMPLATE.substitute(name=declaration['name'])
 | 
			
		||||
        return MODULE_METHODS_TEMPLATE.substitute(METHODS=module_methods)
 | 
			
		||||
 | 
			
		||||
    def get_type_unpack(self, arg, option):
 | 
			
		||||
        return self.TYPE_UNPACK.get(arg['type'], None)
 | 
			
		||||
 | 
			
		||||
    def get_type_check(self, arg, option):
 | 
			
		||||
        return self.TYPE_CHECK.get(arg['type'], None)
 | 
			
		||||
 | 
			
		||||
    def get_wrapper_template(self, declaration):
 | 
			
		||||
        arg_desc = []
 | 
			
		||||
 | 
			
		||||
        def describe_arg(arg):
 | 
			
		||||
            desc = self.TYPE_NAMES[arg['type']] + ' ' + arg['name']
 | 
			
		||||
            if arg.get('nullable'):
 | 
			
		||||
                return '[{} or None]'.format(desc)
 | 
			
		||||
            return desc
 | 
			
		||||
        for option in declaration['options']:
 | 
			
		||||
            option_desc = [describe_arg(arg)
 | 
			
		||||
                           for arg in option['arguments']
 | 
			
		||||
                           if not arg.get('ignore_check', False)]
 | 
			
		||||
            if option_desc:
 | 
			
		||||
                arg_desc.append('({})'.format(', '.join(option_desc)))
 | 
			
		||||
            else:
 | 
			
		||||
                arg_desc.append('no arguments')
 | 
			
		||||
        arg_desc.sort(key=len)
 | 
			
		||||
        arg_desc = ['"' + desc + '"' for desc in arg_desc]
 | 
			
		||||
        arg_str = ', '.join(arg_desc)
 | 
			
		||||
        return Template(self.WRAPPER_TEMPLATE.safe_substitute(expected_args=arg_str))
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user